1. Introduction

Regional urban development is influenced by various stakeholders such as developers, real estate buyers, tenants, planners, and regulators, each pursuing their own objectives. To ensure economic productivity and sustainability, land use planning must consider both supply and demand-side insights.

This project focuses on the Charlottesville Metropolitan Statistical Area (MSA) as a case study, examining how a sprawling metropolitan area can balance economic growth with environmental sustainability by projecting land cover change. The project draws on data from sources such as the US Geological Survey (USGS), Census demographics, and Open Street Maps transportation infrastructure, as well as spatial lag features derived from land cover change, to better understand the relationship between development demand and environmentally sensitive land.

Through exploratory analysis, a geospatial predictive model is developed, trained on land cover changes from 2001-2019. This model is used to estimate development demand for the year 2040, while considering the impact on the environment and landscape fragmentation. The model’s predictions are then used to guide new development in areas that support economic growth without compromising sustainability goals.

1.2. Setup

Below we load required libraries, mapTheme and plotTheme for consistent styling, and specify a palette of colors for visualizations

library(tigris)
library(tidyverse)
library(sf)
library(raster)
library(knitr)
library(kableExtra)
library(tidycensus)
library(tigris)
library(FNN)
#library(QuantPsyc) # JE Note: in R 4.1, QuantPsyc package not available.
library(caret)
library(yardstick)
library(pscl)
library(plotROC) 
library(ggrepel)
library(pROC)
library(grid)
library(gridExtra)
library(viridis)
library(igraph)
library(mapview)
library(exactextractr)


plotTheme <- theme(
  plot.title =element_text(size=12),
  plot.subtitle = element_text(size=8),
  plot.caption = element_text(size = 6),
  axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
  axis.text.y = element_text(size = 10),
  axis.title.y = element_text(size = 10),
  # Set the entire chart region to blank
  panel.background=element_blank(),
  plot.background=element_blank(),
  #panel.border=element_rect(colour="#F0F0F0"),
  # Format the grid
  panel.grid.major=element_line(colour="#D0D0D0",size=.75),
  axis.ticks=element_blank())

mapTheme <- theme(plot.title =element_text(size=12),
                  plot.subtitle = element_text(size=8),
                  plot.caption = element_text(size = 6),
                  axis.line=element_blank(),
                  axis.text.x=element_blank(),
                  axis.text.y=element_blank(),
                  axis.ticks=element_blank(),
                  axis.title.x=element_blank(),
                  axis.title.y=element_blank(),
                  panel.background=element_blank(),
                  panel.border=element_blank(),
                  panel.grid.major=element_line(colour = 'transparent'),
                  panel.grid.minor=element_blank(),
                  legend.direction = "vertical", 
                  legend.position = "right",
                  plot.margin = margin(1, 1, 1, 1, 'cm'),
                  legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))

palette1 <- c("#cd0000","#0059c7")
palette2 <- c("#41b6c4","#253494")
palette4 <- c("#a1dab4","#41b6c4","#2c7fb8","#253494")
palette5 <- c("#ffffcc","#a1dab4","#41b6c4","#2c7fb8","#253494")
palette10 <- c("#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4",
               "#4eb3d3","#2b8cbe","#0868ac","#084081","#f7fcf0")

We also include several helper functions. quintilesBreaks takes a dataframe and a column and outputs the quintiles breaks, helping shorten the ggplot calls below.

It takes longer to ggplot a polygon fishnet with geom_sf than it does to plot geom_point. To cut down on plotting time, the xyC (for ‘XY Coordinates’) takes a fishnet sf and converts it to a dataframe of grid cell centroid coordinates.

rast is a function allowing us to quickly plot raster values in ggplot.

#this function converts a column in to quintiles. It is used for mapping.
quintileBreaks <- function(df,variable) {
    as.character(quantile(df[[variable]],
                          c(.01,.2,.4,.6,.8),na.rm=T))
}

#This function can be used to convert a polygon sf to centroids xy coords.
xyC <- function(aPolygonSF) {
  as.data.frame(
    cbind(x=st_coordinates(st_centroid(aPolygonSF))[,1],
          y=st_coordinates(st_centroid(aPolygonSF))[,2]))
} 

#this function convert a raster to a data frame so it can be plotted in ggplot
rast <- function(inRaster) {
  data.frame(
    xyFromCell(inRaster, 1:ncell(inRaster)), 
    value = getValues(inRaster)) }

2. Data Wrangling & Feature Engineering

In this section a considerable amount of vector and raster data is wrangled together into a regression-ready dataset. The following datasets are used:

2.1 - 2.2: Land cover data downloaded from the Multi-Resolution Land Characteristics Consortium’s National Land Cover Database (NLCD) includes annual land cover and land cover change raster data for the Charlottesville and Albemarle countries. These data are sampled to a 4,000 by 4,000 ft^2 fishnet.

2.3: Population data is downloaded from the U.S. Census and joined to the fishnet by distributing Block population totals proportionally to each grid cell.

2.4: Highway vectors are downloaded from Open Street Map data via GeoFabrik.de, cleaned in ArcGIS Pro, and exported for proximity to highway analysis in R.

2.5: Slope values are derived from 12.5m resolution DEM in ArcGIS Pro and imported to R for inclusion in models.

2.6: Land cover change data from NLCD is used to engineer spatial lag features.

2.7: County polygons are downloaded using the tigris package.

2.8: Features are wrangled into a final dataset.

Data from each of these datasets is integrated into the vector fishnet to provide a comprehensive snapshot of the development process and context in and around Charlottesville between 2001 and 2019.

2.1. Land Cover Change Data

We aim to forecast the dependent variable of land cover change between 2001 and 2019. In this section, we load the land cover raster data, reclassify it, and integrate it with a vector fishnet. This allows us to parameterize spatial relationships in a regression context.

The table below shows descriptions of each categorical land cover type in the land cover data. Below, we will reclassify these data into more useful categories.

Class Value Classification Description
Water
11 Open Water - areas of open water, generally with less than 25% cover of vegetation or soil.
12 Perennial Ice/Snow - areas characterized by a perennial cover of ice and/or snow, generally greater than 25% of total cover.
Developed
21 Developed, Open Space - areas with a mixture of some constructed materials, but mostly vegetation in the form of lawn grasses. Impervious surfaces account for less than 20% of total cover. These areas most commonly include large-lot single-family housing units, parks, golf courses, and vegetation planted in developed settings for recreation, erosion control, or aesthetic purposes.
22 Developed, Low Intensity - areas with a mixture of constructed materials and vegetation. Impervious surfaces account for 20% to 49% percent of total cover. These areas most commonly include single-family housing units.
23 Developed, Medium Intensity - areas with a mixture of constructed materials and vegetation. Impervious surfaces account for 50% to 79% of the total cover. These areas most commonly include single-family housing units.
24 Developed High Intensity - highly developed areas where people reside or work in high numbers. Examples include apartment complexes, row houses and commercial/industrial. Impervious surfaces account for 80% to 100% of the total cover.
Barren
31 Barren Land (Rock/Sand/Clay) - areas of bedrock, desert pavement, scarps, talus, slides, volcanic material, glacial debris, sand dunes, strip mines, gravel pits and other accumulations of earthen material. Generally, vegetation accounts for less than 15% of total cover.
Forest
41 Deciduous Forest - areas dominated by trees generally greater than 5 meters tall, and greater than 20% of total vegetation cover. More than 75% of the tree species shed foliage simultaneously in response to seasonal change.
42 Evergreen Forest - areas dominated by trees generally greater than 5 meters tall, and greater than 20% of total vegetation cover. More than 75% of the tree species maintain their leaves all year. Canopy is never without green foliage.
43 Mixed Forest - areas dominated by trees generally greater than 5 meters tall, and greater than 20% of total vegetation cover. Neither deciduous nor evergreen species are greater than 75% of total tree cover.
Shrubland
51 Dwarf Scrub - Alaska only areas dominated by shrubs less than 20 centimeters tall with shrub canopy typically greater than 20% of total vegetation. This type is often co-associated with grasses, sedges, herbs, and non-vascular vegetation.
52 Shrub/Scrub - areas dominated by shrubs; less than 5 meters tall with shrub canopy typically greater than 20% of total vegetation. This class includes true shrubs, young trees in an early successional stage or trees stunted from environmental conditions.
Herbaceous
71 Grassland/Herbaceous - areas dominated by gramanoid or herbaceous vegetation, generally greater than 80% of total vegetation. These areas are not subject to intensive management such as tilling, but can be utilized for grazing.
72 Sedge/Herbaceous - Alaska only areas dominated by sedges and forbs, generally greater than 80% of total vegetation. This type can occur with significant other grasses or other grass-like plants, and includes sedge tundra, and sedge tussock tundra.
73 Lichens - Alaska only areas dominated by fruticose or foliose lichens generally greater than 80% of total vegetation.
74 Moss - Alaska only areas dominated by mosses, generally greater than 80% of total vegetation.
Planted/Cultivated
81 Pasture/Hay - areas of grasses, legumes, or grass-legume mixtures planted for livestock grazing or the production of seed or hay crops, typically on a perennial cycle. Pasture/hay vegetation accounts for greater than 20% of total vegetation.
82 Cultivated Crops - areas used for the production of annual crops, such as corn, soybeans, vegetables, tobacco, and cotton, and also perennial woody crops such as orchards and vineyards. Crop vegetation accounts for greater than 20% of total vegetation. This class also includes all land being actively tilled.
Wetlands
90 Woody Wetlands - areas where forest or shrubland vegetation accounts for greater than 20% of vegetative cover and the soil or substrate is periodically saturated with or covered with water.
95 Emergent Herbaceous Wetlands - Areas where perennial herbaceous vegetation accounts for greater than 80% of vegetative cover and the soil or substrate is periodically saturated with or covered with water.

Several raster layers have been provided for this analysis:

  • We read in CvilleMSA - this is a fishnet covering the extent of our study area

  • lc_change is a raster of land cover change - where there were conversions between one land cover and another on the time frame 2001-2019. We plot the raster using ggplot and the rast function specified above.

Note that lc_change is projected as NAD 1983 StatePlane Virginia South FIUS and is spatially referenced as EPSG:2284. It has also been clipped in ArcGIS to the boundary of our study area.

CvilleMSA <-
  # High-Resolution Version:
  st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_Hex.geojson") %>%
  # Medium-Resolution Version:
  # st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/a3649c5ff06585500c219b3990991065ad3c0a6d/data/Cville_Tesselated_Coarse.geojson") %>%
  # Low-Resolution Version:
  # st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/a3649c5ff06585500c219b3990991065ad3c0a6d/data/Cville_Tesselated_Coarser.geojson") %>%
  st_transform('EPSG:2284')

lc_change = raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/LC_Change_2001_2019.tif")
  
lc_change <- projectRaster(lc_change, crs = CRS("+init=epsg:2284"))

We now plot Land Cover Change within our MSA.

ggplot() +
  geom_raster(data=rast(lc_change) %>% na.omit %>% filter(value > 0), 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(direction = -1, discrete=TRUE, name ="Land Cover\nChange") +
  labs(title = "Land Cover Change, 2000-2019") +
  mapTheme +
  theme(legend.direction="horizontal")

Next, we reclassify the raster such that all the developed grid cell values receive a value of 1 and all other values receive a value of 0.

# This is done using a reclassify matrix. The matrix reads row by row. Row 1 says any grid cell ranging from 0 to 12 takes a value of 0; 13 or greater through 24, a value of 1; and all other values take 0.
reclassMatrix <-  matrix(c(
    0,2,0,
    2,3,1,
    3,Inf,0),
  ncol=3, byrow=T)

reclassMatrix
##      [,1] [,2] [,3]
## [1,]    0    2    0
## [2,]    2    3    1
## [3,]    3  Inf    0

Now we reclassify and convert all 0’s to NA. We apply a name to the raster with names. This is done to make it faster to join raster to the fishnet below.

lc_change2 <- 
  reclassify(lc_change,reclassMatrix)

lc_change2[lc_change2 < 1] <- NA

names(lc_change2) <- "lc_change"

ggplot() +
  geom_sf(data=CvilleMSA, fill = "grey", color = "transparent")+
  geom_raster(data=rast(lc_change2) %>% na.omit, 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE, name ="Land Cover\nChange") + 
  labs(title="Development Land Use Change") +
  mapTheme

The code below converts the raster to an sf point layer and then joins the points to the fishnet with aggregate. Finally, the fishnet variable lc_change is created that is 1 where new development has occurred and 0 where it has not. This is our dependent variable and encoded as a factor.

changePoints <-
  rasterToPoints(lc_change2) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(CvilleMSA))

fishnet <- 
  aggregate(changePoints, CvilleMSA, sum) %>%
  mutate(lc_change = ifelse(is.na(lc_change),0,1),
         lc_change = as.factor(lc_change))
# Plot
# To speed up the mapping process, fishnet polygons are converted to centroid points using the `xyC` function defined earlier  
ggplot() +
  geom_sf(data=fishnet, aes(fill=lc_change), color=NA) +
  scale_fill_manual(values = palette2, labels=c("No Change","New Development"), name = "") +
  labs(title = "Land Cover Development Change", subtitle = "As fishnet centroids") +
  mapTheme

2.2. Land Cover in 2001

It is reasonable to hypothesize that the propensity of new development is a function of existing land cover categories. In this section we identify these other land cover categories from 2001 and integrate each with the fishnet.

lc_2001 <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_LC_2001.tif")

ggplot() +
  geom_raster(data=rast(lc_2001) %>% na.omit %>% filter(value > 0), 
              aes(x,y,fill=as.factor(value))) +
  scale_fill_viridis(discrete=TRUE, name ="") +
  labs(title = "Land Cover, 2001") +
  mapTheme +
  theme(legend.direction="horizontal")

The table below shows the approach taken to recode existing land cover codes into the categories used in our analysis. In the code block below new rasters are generated and names are applied. Naming ensures that when the raster is integrated with the fishnet, the column reflects the appropriate raster.

Old_Classification New_Classification
Open Space as well as Low, Medium and High Intensity Development Developed
Deciduous, Evergreen, and Mixed Forest Forest
Pasture/Hay and Cultivated Crops Farm
Woody and Emergent Herbaceous Wetlands Woodlands
Barren Land, Dwarf Scrub, and Grassland/Herbaceous Other Undeveloped
Water Water
developed <- lc_2001 == 21 | lc_2001 == 22 | lc_2001 == 23 | lc_2001 == 24
forest <- lc_2001 == 41 | lc_2001 == 42 | lc_2001 == 43 
farm <- lc_2001 == 81 | lc_2001 == 82 
wetlands <- lc_2001 == 90 | lc_2001 == 95 
otherUndeveloped <- lc_2001 == 52 | lc_2001 == 71 | lc_2001 == 31 
water <- lc_2001 == 11

names(developed) <- "developed"
names(forest) <- "forest"
names(farm) <- "farm"
names(wetlands) <- "wetlands"
names(otherUndeveloped) <- "otherUndeveloped"
names(water) <- "water"

Next, each raster is aggregated to the fishnet by way of a function called aggregateRaster. Here, the process used above to To do this, a function is created below that loops through a list of rasters, converts the ith raster to points, filters only points that have value of 1 (ie. is the ith land cover type), and then aggregates to the fishnet.

Here is the function.

aggregateRaster <- function(inputRasterList, theFishnet) {
  #create an empty fishnet with the same dimensions as the input fishnet
  theseFishnets <- theFishnet %>% dplyr::select()
  #for each raster in the raster list
  for (i in inputRasterList) {
  #create a variable name corresponding to the ith raster
  varName <- names(i)
  #convert raster to points as an sf
    thesePoints <-
      rasterToPoints(i) %>%
      as.data.frame() %>%
      st_as_sf(coords = c("x", "y"), crs = st_crs(theFishnet)) %>%
      filter(.[[1]] == 1)
  #aggregate to the fishnet
    thisFishnet <-
      aggregate(thesePoints, theFishnet, length) %>%
      mutate(!!varName := ifelse(is.na(.[[1]]),0,1))
  #add to the larger fishnet
    theseFishnets <- cbind(theseFishnets,thisFishnet)
  }
  #output all aggregates as one large fishnet
   return(theseFishnets)
  }

The theRasterList of land cover types in 2001 is created and then fed into aggregateRaster. The result is converted to long form grid cell centroids and plot as small multiple maps.

Note that since our land cover data is at a different resolution than our fishnet, we have reassigned cells with more than one land cover value to just one based on a hierarchy. This method can introduce some degree of error, which is worse with lower-resolution fishnets. For our purposes, using a high-resolution fishnet, it performs just fine. Later iterations of this workflow will include a method for assigning the most common land cover type within each fishnet cell to that cell, which should reduce the degree of error that comes with lower-resolution fishnets.

Note also the inclusion of st_cast here which convert all geometries to POLYGON. If we create a frequency table of geometry types in aggregatedRasters, we will notice some and handful of MULTIPOLYGONS. Try table(st_geometry_type(aggregatedRasters)). These rogue multipolygons break the xyC function which is designed to find grid cell centroids. After all, there is no one centroid of several combined polygons. Thus st_cast ensures all geometries are just POLYGON.

theRasterList <- c(developed,forest,farm,wetlands,otherUndeveloped,water)

aggregatedRasters <-
  aggregateRaster(theRasterList, CvilleMSA) %>%
  dplyr::select(developed,forest,farm,wetlands,otherUndeveloped,water) %>%
  mutate_if(is.numeric,as.factor)

# reassign cells with more than one lc value to just one based on hierarchy shown here
aggregatedRasters <- aggregatedRasters %>%
    mutate(farm = ifelse(developed == 0 & farm == 1, 1, 0)) %>%
    mutate(forest = ifelse(developed == 0 & farm == 0 & forest == 1, 1, 0)) %>%
    mutate(wetlands = ifelse(developed == 0 & farm == 0 & forest == 0 & wetlands == 1, 1, 0)) %>%
    mutate(water = ifelse(developed == 0 & farm == 0 & forest == 0 & wetlands == 0 & water == 1, 1, 0)) %>%
    mutate(otherUndeveloped = ifelse(developed == 0 & farm == 0 & forest == 0 & wetlands == 0 & water == 0 & otherUndeveloped == 1, 1, 0))

aggregatedRasters %>%
  gather(var,value,developed:water) %>%
  st_cast("POLYGON") %>%    #just to make sure no weird geometries slipped in
  mutate(X = xyC(.)$x,
         Y = xyC(.)$y) %>%
  ggplot() +
    geom_sf(data=CvilleMSA) +
    geom_point(aes(X,Y, colour=as.factor(value)), size =0.1) +
    facet_wrap(~var) +
    scale_colour_manual(values = palette2,
                        labels=c("Other","Land Cover"),
                        name = "") +
    labs(title = "Land Cover Types, 2001",
         subtitle = "As fishnet centroids") +
   mapTheme

2.3. Census Data

Population and population change are crucial demand-side factors for predicting Development_Demand. We obtain 2000 and 2020 census data through the tidycensus package to represent the demographic features for 2001 and 2019. These data are downloaded at a block group geography and thus an approach is needed to reconcile tracts and fishnet geometries. This is accomplished using a technique called areal weighted interpolation.

Below we get the demographic data including total_population, total_housing_units, total_vacant_households, total_white,total_graduate_degree_holders for both 2000 and 2020.

#function to get and clean 2000 census data (decennial)
clean_2000_census_data <- function() {
  
  #varibles to use for 2000 decenital variables
  variables2000A <- c("P001001", "H001001", "H003003", "P003003")
  names(variables2000A) <- c("total_population", "total_housing_units", "total_vacant_households", "total_white")
  
  variables2000B <- c("P053001","P036021","P036044")
  names(variables2000B) <- c("median_household_income","Male Total graduate degree holders", "Female Total graduate degree holders")
  
  #function to get 2000 decennial variables
  get_decenial_variables <- function(year, sumfile, variables, county) {
    data <- get_decennial(
      geography = "block group",
      year = year,
      sumfile = sumfile,
      variables = variables,
      state = "VA",
      county = county,
      output = "wide",
      geometry = TRUE
    )
    
    return(data)
  }
  
  # get variables from different decennial files for CV and AM
  data_cv1 <- get_decenial_variables(2000,'sf1', variables2000A, "Charlottesville")
  data_cv2 <- get_decenial_variables(2000,'sf3', variables2000B, "Charlottesville")
  data_am1 <- get_decenial_variables(2000,'sf1', variables2000A, "Albemarle")
  data_am2 <- get_decenial_variables(2000,'sf3', variables2000B, "Albemarle")
  
  # join the data
  cvam_census1 <- rbind(data_cv1, data_am1)
  cvam_census2 <- rbind(data_cv2, data_am2)
  
  cvam_census <- st_join(cvam_census1, 
                         cvam_census2[, c("median_household_income", "Male Total graduate degree holders", "Female Total graduate degree holders")], 
                         join = st_equals)  %>%
    mutate(`total_graduate_degree_holders` = `Male Total graduate degree holders` + `Female Total graduate degree holders`) %>%
    dplyr::select(-`Male Total graduate degree holders`, -`Female Total graduate degree holders`)
  
  return(cvam_census)
}



#function to get and clean 2020 census data (acs5)
clean_2020_census_data <- function() {
  
  #varibles to use for 2020 decenital variables
  variables2020A <- c("B01003_001E", "B25001_001E", "B25002_003E", "B02001_002E")
  names(variables2020A) <- c("total_population", "total_housing_units", "total_vacant_households", "total_white")
  
  variables2020B <- c("B19049_001E","B14002_022E","B14002_046E")
  names(variables2020B) <- c("median_household_income","Male Total graduate degree holders", "Female Total graduate degree holders")
  
  #function to get 2020 decenital variables
  get_acs_variables <- function(year, variables, county) {
    data <- get_acs(
      geography = "block group",
      survey = "acs5",
      year = year,
      variables = variables,
      state = "VA",
      county = county,
      output = "wide",
      geometry = TRUE
    )
    
    return(data)
  }
  
  # get variables from different acs files for CV and AM
  data_cv1 <- get_acs_variables(2020, variables2020A, "Charlottesville") %>% dplyr::select(!ends_with("M"))
  data_cv2 <- get_acs_variables(2020, variables2020B, "Charlottesville") %>% dplyr::select(!ends_with("M"))
  data_am1 <- get_acs_variables(2020, variables2020A, "Albemarle") %>% dplyr::select(!ends_with("M"))
  data_am2 <- get_acs_variables(2020, variables2020B, "Albemarle") %>% dplyr::select(!ends_with("M"))
  
  # join the data
  cvam_census1 <- rbind(data_cv1, data_am1)
  cvam_census2 <- rbind(data_cv2, data_am2)
  
  cvam_census <- st_join(cvam_census1, 
                         cvam_census2[, c("median_household_income", "Male Total graduate degree holders", "Female Total graduate degree holders")], 
                         join = st_equals)  %>%
    mutate(`total_graduate_degree_holders` = `Male Total graduate degree holders` + `Female Total graduate degree holders`) %>%
    dplyr::select(-`Male Total graduate degree holders`, -`Female Total graduate degree holders`)
  
  return(cvam_census)
}


# run the functions 
cvam_2000_census <- clean_2000_census_data() # 2000 census data
cvam_2020_census <- clean_2020_census_data() # 2020 census data

Additional Census Data Formatting for 2020.

cvam_2020_census <- cvam_2020_census %>% 
  mutate(total_population2020 = total_population) %>%
  mutate(total_housing_units2020 = total_housing_units) %>%
  mutate(total_vacant_households2020 = total_vacant_households) %>%
  mutate(total_white2020 = total_white) %>%
  mutate(median_household_income2020 = median_household_income) %>%
  mutate(total_graduate_degree_holders2020 = total_graduate_degree_holders) %>%
  dplyr::select(-`total_population`, -`total_housing_units`, -`total_vacant_households`, -`total_white`, -`median_household_income`, -`total_graduate_degree_holders`)

Feature of 2000 total population data is plotted.

ggplot()+
  geom_sf(data = cvam_2000_census, 
          aes(fill = total_population)) + mapTheme

To join the demographic data to fishnet, a spatial join would be inappropriate as it would assign the same demographic feature value from one tract to the many intersecting grid cells. Instead, the area weighted interpolation function, st_interpolate_aw, assigns a proportion of a tract’s demographic feature to a grid cell weighted by the proportion of the tract that intersects the grid cell. This works best when we assume that the block group population is uniformly distributed across the block group. This is typically not a great assumption. However, it is a reasonable here particularly given demographic features in a regression and not an outcome that needs to be measured with significant precision.

CvilleMSA <-
  CvilleMSA %>%
  rownames_to_column("fishnetID") %>% 
  mutate(fishnetID = as.numeric(fishnetID)) %>%
  dplyr::select(fishnetID)

# Transform Projection of Census Data
cvam_2000_census <- cvam_2000_census %>% 
  st_transform(st_crs(CvilleMSA))

cvam_2020_census <- cvam_2020_census %>% 
  st_transform(st_crs(CvilleMSA))
# Join interpolated census data with fishnet

# 2000 interpolation function
interpolate_column2000 <- function(column_name) {
  
  interpolated_data <-
    st_interpolate_aw(cvam_2000_census[column_name], CvilleMSA, extensive=TRUE) %>%
    as.data.frame(.) %>%
    rownames_to_column(var = "fishnetID") %>%
    left_join(CvilleMSA %>%
                mutate(fishnetID = as.character(fishnetID)),
              ., by=c("fishnetID"='fishnetID')) %>% 
    mutate(!!column_name := replace_na(!!sym(column_name), 0)) %>%
    dplyr::select(column_name)
  
  return(interpolated_data)
}

fishnetPopulation00 <- interpolate_column2000('total_population')
fishnetHHunit00 <- interpolate_column2000('total_housing_units')
fishnetVacHH00 <- interpolate_column2000('total_vacant_households')
fishnetWhiteP00 <- interpolate_column2000('total_white')
fishnetGraduate00 <- interpolate_column2000('total_graduate_degree_holders')

# 2020 interpolation function
interpolate_column2020 <- function(column_name) {
  
  interpolated_data <-
    st_interpolate_aw(cvam_2020_census[column_name], CvilleMSA, extensive=TRUE) %>%
    as.data.frame(.) %>%
    rownames_to_column(var = "fishnetID") %>%
    left_join(CvilleMSA %>%
                mutate(fishnetID = as.character(fishnetID)),
              ., by=c("fishnetID"='fishnetID')) %>% 
    mutate(!!column_name := replace_na(!!sym(column_name), 0)) %>%
    dplyr::select(column_name)
  
  return(interpolated_data)
}

fishnetPopulation20 <- interpolate_column2020('total_population2020')
fishnetHHunit20 <- interpolate_column2020('total_housing_units2020')
fishnetWhiteP20 <- interpolate_column2020('total_white2020')

fishnet_pop_change_00_20 <- 
  cbind(fishnetPopulation00,fishnetPopulation20) %>%
  dplyr::select(total_population,total_population2020) %>%
  mutate(pop_Change_00_20 = total_population2020 - total_population)

#plot data from 2000
ggplot()+
  geom_sf(data = fishnetPopulation00, 
          aes(fill = total_population), color = "transparent")+ mapTheme

2.4. Highway Distance

Accessibility is a key determinant of development potential particularly in a sprawling city like Charlottesville. For the purposes of this workflow, accessibility features are engineered by measuring distance from each grid cell to its nearest highway.

First highway vectors are imported in geojson format; projected and subset to the subset using st_intersection. Below, new development is mapped with the highway overlay.

CvilleHighways <-
  st_read("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/b528d725eaea5dd778c41fa644b5e6f71d6291b2/data/Cville_Highways.geojson") %>%
  st_transform(st_crs(CvilleMSA)) %>%
  st_intersection(CvilleMSA)

ggplot() +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2],colour=lc_change),size=0.1) +
  geom_sf(data=CvilleHighways, size = 1) +
  scale_colour_manual(values = palette2,
                      labels=c("No Change","New Development")) +
  labs(title = "New Development and Highways",
       subtitle = "As fishnet centroids") +
  mapTheme

The distance from each grid cell to its nearest highway segment is measured.

First, the highway layer is converted to raster. This is done by creating an emptyRaster of NA grid cells at the same spatial extent as lc_change. Then, highway_raster is created by converting CvilleHighways to sp form and then to applying rasterize. The raster is then converted to points with rasterToPoints and st_as_sf, then aggregate is used to calculate mean distance by grid cell.

emptyRaster <- lc_change
emptyRaster[] <- NA

highway_raster <- 
  as(CvilleHighways,'Spatial') %>%
  rasterize(.,emptyRaster)

highway_raster_distance <- distance(highway_raster)
names(highway_raster_distance) <- "distance_highways"

highwayPoints <-
  rasterToPoints(highway_raster_distance) %>%
  as.data.frame() %>%
  st_as_sf(coords = c("x", "y"), crs = st_crs(CvilleMSA))

highwayPoints_fishnet <- 
  aggregate(highwayPoints, CvilleMSA, mean) %>%
  mutate(distance_highways = ifelse(is.na(distance_highways),0,distance_highways))

ggplot() +
  geom_sf(data=CvilleMSA) +
  geom_point(data=highwayPoints_fishnet, aes(x=xyC(highwayPoints_fishnet)[,1], 
                                             y=xyC(highwayPoints_fishnet)[,2], 
                 colour=factor(ntile(distance_highways,5))),size=1.5) +
  scale_colour_manual(values = palette5,
                      labels=substr(quintileBreaks(highwayPoints_fishnet,"distance_highways"),1,8),
                      name="Quintile\nBreaks") +
  geom_sf(data=CvilleHighways, colour = "red") +
  labs(title = "Distance to Highways",
       subtitle = "As fishnet centroids; Highways visualized in red") +
  mapTheme

2.5. Add Slope Variable

A raster dataset of slope is imported and its data is integrated into the fishnet using ‘exactextractr’.

SlopeRaster <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/9f810ec8e1e9774c51d83194e22848e8240d26f7/data/Cville_Slope.tif")

# Check the CRS of the raster and the fishnet data
raster_crs <- crs(SlopeRaster)
fishnet_crs <- st_crs(fishnet)

# If CRS doesn't match, reproject the fishnet data to match the raster CRS
if (raster_crs != fishnet_crs) {
  fishnet <- st_transform(fishnet, raster_crs)}

# Calculate the mean raster values within each fishnet cell
mean_values <- exact_extract(SlopeRaster, fishnet, fun = 'mean')

# Add the mean values as a new column to the fishnet data
fishnet$slope <- mean_values

# Transform the fishnet back EPSG:2284
fishnet <- st_transform(fishnet, 2284)

2.6. The Spatial Lag of Development

Our model hypothesizes that development demand partly depends on existing development patterns. Accessibility plays a significant role in traditional ‘bid-rent’ economic models of development. However, this model assumes shared preferences for central city access, which may not hold true in sprawling regions like Charlottesville MSA, where suburban locations are desirable.

To forecast growth, features must be created to associate these patterns with development. Accessibility is measured via spatial lag, hypothesizing that new development depends on distance to existing development. The average distance from each grid cell to its two nearest developed neighboring grid cells in 2001 is calculated using the nn_function.

# The function below calculates average nearest neighbor distance between k point layers. The first parameter specifies coordinates that we want to `measureFrom`, in this case, `fishnet` centroids. The second, indicates the point layer we wish to `measureTo`.

nn_function <- function(measureFrom,measureTo,k) {
  #convert the sf layers to matrices
  measureFrom_Matrix <-
    as.matrix(measureFrom)
  measureTo_Matrix <-
    as.matrix(measureTo)
  nn <-   
    get.knnx(measureTo, measureFrom, k)$nn.dist
    output <-
    as.data.frame(nn) %>%
    rownames_to_column(var = "thisPoint") %>%
    gather(points, point_distance, V1:ncol(.)) %>%
    arrange(as.numeric(thisPoint)) %>%
    group_by(thisPoint) %>%
    summarize(pointDistance = mean(point_distance)) %>%
    arrange(as.numeric(thisPoint)) %>% 
    dplyr::select(-thisPoint) %>%
    pull()
  
  return(output)  
}

Next, lag distance is appended to our fishnet. There are 3 inputs. The fishnet which is converted to a coordinate data frame with the xyC function. 2001 developed areas are created using filter. The map below illustrates relative accessibility from every grid cell to nearby development.

fishnet$lagDevelopment <-
    nn_function(xyC(fishnet),
                xyC(filter(aggregatedRasters,developed==1)),
                2)

ggplot() +
  geom_sf(data=CvilleMSA) +
  geom_point(data=fishnet, 
             aes(x=xyC(fishnet)[,1], y=xyC(fishnet)[,2], 
                 colour=factor(ntile(lagDevelopment,5))), size=0.01) +
  scale_colour_manual(values = palette5,
                     labels=substr(quintileBreaks(fishnet,"lagDevelopment"),1,7),
                     name="Quintile\nBreaks") +
  labs(title = "Spatial Lag to 2001 Development",
       subtitle = "As fishnet centroids") +
  mapTheme

2.7. MSA Counties

The tigris package allows Virginia county geometries to be downloaded. A spatial subset returns only the counties in the MSA.

options(tigris_class = "sf")

studyAreaCounties <- 
  counties("Virginia") %>%
  st_transform(st_crs(CvilleMSA)) %>%
  dplyr::select(NAME) %>%
  .[st_buffer(CvilleMSA,-1), , op=st_intersects] 

2.8. Create the Final Dataset

The last step is to bring together all the disparate feature layers into a final dataset that can be used for analysis. The various fishnet layers are cbind together, needed features are extracted and the final fishnet, dat is then joined with studyAreaCounties to assign each grid cell to a county. developed19 is created to designate those areas that have already been developed through 2019. Finally, any grid cell that has a water land cover designation is removed.

dat <- 
  cbind(
    fishnet, highwayPoints_fishnet,fishnetGraduate00,
    fishnetHHunit00,fishnetPopulation00,fishnetVacHH00,
    fishnetWhiteP00,fishnetPopulation20,fishnet_pop_change_00_20,aggregatedRasters,
    fishnetHHunit20, fishnetWhiteP20) %>%
  dplyr::select(lc_change, developed, forest, farm, wetlands, otherUndeveloped, slope,water,
                total_population,total_graduate_degree_holders,total_housing_units,
                total_vacant_households,total_white,total_population2020,pop_Change_00_20,distance_highways, lagDevelopment, total_housing_units2020, total_white2020) %>%
  st_join(studyAreaCounties) %>%
  mutate(NAME = ifelse(NAME == "Charlottesville", "Charlottesville", "Albemarle")) %>% 
  mutate(developed19lag = ifelse(lc_change == 1 & developed == 1, 0, developed)) %>%
  filter(water == 0) 

studyAreaCounties <- studyAreaCounties %>% 
  filter(NAME == "Albemarle" | NAME == "Charlottesville")
ggplot() +
  geom_sf(data=studyAreaCounties,
          aes(fill = NAME)) +
  labs(title = "Study Area Counties") +
  mapTheme

3. Exploratory Analysis

In this section we explore the extent to which each feature is associated with development change. If the goal was to predict a continuous variable, scatterplots and correlation coefficients make this process straightforward and relatively easy to explain to a non-technical decision maker.

In this case however, the dependent variable is a binary outcome - either a grid cell was developed between 2001 and 2019 or it wasn’t. In this case, the relevant question is whether for a given feature, there is a statistically significant difference between areas that changed and areas that did not. These differences are explored in a set of plots below. For models with lots of features, these plots could be compliment by a series of difference in means statistical tests.

The below code block selects the highways and spatial lag features, converts each to long form and plots each as bar plots. Note that geom_bar calculates the mean. The mean distance_highways is significantly lower for the New Development category compared to the No Change category, it indicates that new developments tend to be closer to highways. On the other hand, the mean lagDevelopment is significantly lower for the New Development category compared to the No Change category, it may imply that new development is more likely to occur in areas that are near existing development.

dat %>%
  dplyr::select(distance_highways,lagDevelopment,lc_change) %>%
  gather(Variable, Value, -lc_change, -geometry) %>%
  ggplot(., aes(lc_change, Value, fill=lc_change)) + 
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable, scales = "free_y") +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="") +
    labs(title="New Development as a Function of the Continuous Variables") +
    plotTheme 

Next, the same visualization is created for the population related variables. The higher mean values for total_population, total_population2020, and pop_Change_00_20 in the New Development category suggest that development is more likely to occur in areas with higher populations and greater population growth.

dat %>%
  dplyr::select(total_population,total_population2020,pop_Change_00_20,lc_change) %>%
  gather(Variable, Value, -lc_change, -geometry) %>%
  ggplot(., aes(lc_change, Value, fill=lc_change)) +
    geom_bar(position = "dodge", stat = "summary", fun.y = "mean") +
    facet_wrap(~Variable) +
    scale_fill_manual(values = palette2,
                      labels=c("No Change","New Development"),
                      name="") +
    labs(title="New Development as a Function of Factor Variables") +
    plotTheme

Next, a table of land cover conversion between 2001 and 2019 is created. The table suggests for instance, that 0.71% of The plots above suggest that the continuous variable (e.g., distance_highways, lagDevelopment, population related variables) has an association with the occurrence of new development.

Next, a table of land cover conversion between 2001 and 2019 is created. The table suggests for instance, that 0.77% of farmland regionally was converted to development between 2001 and 2019.

dat %>%
  dplyr::select(lc_change:otherUndeveloped,developed) %>%
  gather(Land_Cover_Type, Value, -lc_change, -geometry) %>%
   st_set_geometry(NULL) %>%
     group_by(lc_change, Land_Cover_Type) %>%
     summarize(n = sum(as.numeric(Value))) %>%
     ungroup() %>%
    mutate(Conversion_Rate = paste0(round(100 * n/sum(n), 2), "%")) %>%
    filter(lc_change == 1) %>%
  dplyr::select(Land_Cover_Type,Conversion_Rate) %>%
  kable() %>% kable_styling(full_width = F)
Land_Cover_Type Conversion_Rate
developed 6.07%
farm 0.71%
forest 0.43%
otherUndeveloped 0%
wetlands 0%

4. Predicting for 2010

In this section, six separate logistic regression models are estimated to predict development change between 2001 and 2019 - with each subsequent model more sophisticated then the last. To do so, the data is split into 50% training/test sets. Models are estimated on the training set.

For brevity, a less sophisticated approach to describing the accuracy and generalizability of predictions for each model is taken here, judging each by the McFadden or “Psuedo” R Squared statistic on the test set. The model with the greatest goodness of fit is then used for the purposes of prediction.

4.1. Modeling

First, dat is split into training and test sets. Note how imbalanced the panel is with table(datTrain$lc_change1).

set.seed(3456)
trainIndex <- 
  createDataPartition(dat$developed, p = .50,
                                  list = FALSE,
                                  times = 1)
datTrain <- dat[ trainIndex,]
datTest  <- dat[-trainIndex,]

nrow(dat)
## [1] 84086

Next six separate glm models are estimated adding new variables for each. Figure 4.1 shows the Psuedo R-Squared associated with each model.

Model1 includes only the 2001 land cover types. Model2 adds the lagDevelopment. Models 3, 4 and 5 attempt three different approaches for modeling demographic changes, infrastructure and slope; Model3 uses 2000 population and distance to highway, Model4 adds slope to Model3, and Model5 adds population change to Model4. Model6 adds demographic features to Model5.

Model1 <- glm(lc_change ~ wetlands + forest  + farm + otherUndeveloped, 
              family="binomial"(link="logit"), data = datTrain)

Model2 <- glm(lc_change ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment, 
              family="binomial"(link="logit"), data = datTrain)
              
Model3 <- glm(lc_change ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + total_population +
                 distance_highways, 
              family="binomial"(link="logit"), data = datTrain)  

Model4 <- glm(lc_change ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + total_population +
                 distance_highways + slope, 
                family="binomial"(link="logit"), data = datTrain)  

Model5 <- glm(lc_change ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment + total_population +
                 distance_highways + slope + pop_Change_00_20 + total_population2020,
                family="binomial"(link="logit"), data = datTrain)               

Model6 <- glm(lc_change ~ wetlands + forest  + farm + otherUndeveloped + lagDevelopment +
                 distance_highways + slope + pop_Change_00_20
                 + total_housing_units  + total_white,
                family="binomial"(link="logit"), data = datTrain)

Below codes create a data frame of psudeo R Squares for each model and plotting them for comparison. This approach loops through the models retrieving the goodness of fit for each. Model6 is the final model employed for prediction.

modelList <- paste0("Model", 1:6)
map_dfc(modelList, function(x)pR2(get(x)))[4,] %>%
  setNames(paste0("Model",1:6)) %>%
  gather(Model,McFadden) %>%
  ggplot(aes(Model,McFadden)) +
    geom_bar(stat="identity") +
    labs(title= "McFadden R-Squared by Model") +
    plotTheme
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2
## fitting null model for pseudo-r2

Next, a data frame is created that includes columns for the observed development change, lc_change, and one that includes predicted probabilities for Model6. This data frame is then used as an input to a density plot visualizing the distribution of predicted probabilities by observed class. Only a small number of predicted probabilities are greater than or equal to 50% (nrow(filter(testSetProbs, probs >= .50)) / nrow(datTest)). This makes good sense, given how rare of an event development is in our dataset. Ultimately, in order to judge our model with a confusion matrix, a smaller development classification threshold must be employed.

testSetProbs <- 
  data.frame(class = datTest$lc_change,
             probs = predict(Model6, datTest, type="response")) 
  
ggplot(testSetProbs, aes(probs)) +
  geom_density(aes(fill=class), alpha=0.5) +
  scale_fill_manual(values = palette1,
                    labels=c("No Change","New Development")) +
  labs(title = "Histogram of test set predicted probabilities",
       x="Predicted Probabilities",y="Density") +
  plotTheme

4.2. Accuracy

Now to pick a predicted probability threshold to classify an area as having new development. Sensitivity or the True Positive rate is the proportion of actual positives (1’s) that were predicted to be positive. For example, the Sensitivity in our model is the rate of developed areas actually predicted as such. Specificity or True Negative Rate is the proportion of actual negatives (0’s) that were predicted to be negatives. For example, the Specificity in our model is the rate of No Change areas that were correctly predicted as No change.

There are some clear trade-offs between Sensitivity and Specificity in our model that deserve some exploration. To illustrate, two different thresholds of 13% and 30% are explored. Predicted classes for both thresholds are generated and instead of using the confusionMatrix function from caret as we have in the past, here confusion matrix metrics are derived from the yardstick package. This allows us to group_by the threshold and summarize the metrics of interest.

The options call below is required to tell yardstick that the positive factor class in testSetProbs is 1. Without it, yardstick will by default see the first factor level as 0 and flip the confusion metrics around.

options(yardstick.event_first = FALSE)

testSetProbs <- 
  testSetProbs %>% 
  mutate(predClass_05 = as.factor(ifelse(testSetProbs$probs >= 0.05 ,1,0)),
         predClass_20 = as.factor(ifelse(testSetProbs$probs >= 0.2 ,1,0))) 

testSetProbs %>%
  dplyr::select(-probs) %>%
  gather(Variable, Value, -class) %>%
  group_by(Variable) %>%
  summarize(Sensitivity = round(yardstick::sens_vec(class,factor(Value)),2),
            Specificity = round(yardstick::spec_vec(class,factor(Value)),2),
            Accuracy = round(yardstick::accuracy_vec(class,factor(Value)),2)) %>% 
  kable() %>%
  kable_styling(full_width = F)
Variable Sensitivity Specificity Accuracy
predClass_05 0.89 0.68 0.69
predClass_20 0.47 0.94 0.91

The 13% threshold correctly predicts a higher number of new development areas (Sensitivity), but incorrectly predicts a lower number of no change areas (Specificity). As there are far more no change areas in the data, this is reflected in a lower overall accuracy. Conversely, the 30% threshold has a lower Sensitivity rate and but a far higher Specificity rate. Again, because of the dataset is majority no change areas, this leads to a far higher Accuracy rate.

Given the use case, and the spatial distribution of land cover change, it may be more useful to have a model that predicts generally where new development occurs rather than one that predicts precisely where. As illustrated below, the 20% threshold provides this outcome. These trade-offs can be visualized in the plot below. Here the model is used to predict for the entire dat dataset. 20% threshold looks more reasonable given the distribution of observed development change.

predsForMap <-         
  dat %>%
    mutate(probs = predict(Model6, dat, type="response") ,
           Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_20_Pct =  as.factor(ifelse(probs >= 0.20 ,1,0))) %>%
    dplyr::select(lc_change,Threshold_5_Pct,Threshold_20_Pct) %>%
    gather(Variable,Value, -geometry) %>%
    st_cast("POLYGON")
ggplot() +
  geom_point(data=predsForMap, aes(x=xyC(predsForMap)[,1], y=xyC(predsForMap)[,2], colour=Value)) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = palette2, labels=c("No Change","New Development"),
                      name="") +
  labs(title="Development Predictions - Low Threshold") + 
  mapTheme

To provide a bit more insight, the code block below produces both true positives (Sensitivity) and true negatives (Specificity) for each grid cell by threshold type.

ConfusionMatrix.metrics <-
  dat %>%
    mutate(probs = predict(Model3, dat, type="response") ,
           Threshold_5_Pct = as.factor(ifelse(probs >= 0.05 ,1,0)),
           Threshold_20_Pct =  as.factor(ifelse(probs >= 0.20 ,1,0))) %>%
    mutate(TrueP_05 = ifelse(lc_change  == 1 & Threshold_5_Pct == 1, 1,0),
           TrueN_05 = ifelse(lc_change  == 0 & Threshold_5_Pct == 0, 1,0),
           TrueP_20 = ifelse(lc_change  == 1 & Threshold_20_Pct == 1, 1,0),
           TrueN_20 = ifelse(lc_change  == 0 & Threshold_20_Pct == 0, 1,0)) %>%
    dplyr::select(., starts_with("True")) %>%
    gather(Variable, Value, -geometry) %>%
    st_cast("POLYGON") 
ggplot(data=ConfusionMatrix.metrics) +
  geom_point(aes(x=xyC(ConfusionMatrix.metrics)[,1], 
                 y=xyC(ConfusionMatrix.metrics)[,2], colour = as.factor(Value))) +
  facet_wrap(~Variable) +
  scale_colour_manual(values = palette2, labels=c("Correct","Incorrect"),
                       name="") +
  labs(title="Development Predictions - Low Threshold") + mapTheme

5. Predicting Land Cover Demand for 2040

5.1. Update Population Change and Lag Development values

At this point, a simple but useful model has been trained to predict urban development between 2001 and 2019 as a function of baseline features from 2001 including land cover, built environment and population. Next, we are going to update our features to reflect a 2019 baseline. Having done so, predictions from our new model would then be for 2040.

For brevity, we only update two features in our model for now. First, population change (pop_change) is updated using county level population projections visualized in the plot below. The second is lagDevelopment, which describes how predicted new development relates in space to old development.

Once the features are updated, 2040 predictions are estimated and mapped.

Below, lagDevelopment is mutated to describe average distance to 2019 development. Note that the field name, lagDevelopment is unchanged (ie. not updated to lagDevelopment_2019). This is done purposefully as model6 has a regression coefficient called lagDevelopment. If this variable wasn’t present in our updated data frame then the predict command would fail.

dat <-
  dat %>%
  mutate(lagDevelopment = nn_function(xyC(.), xyC(filter(.,developed19lag == 2)),2))

Now to update population change. A new data frame, countyPopulation_2040 is created which includes 2020 population counts and 2040 projections for each county in the study area. Population is plotted by year and by county.

countyPopulation_2040 <- 
  data.frame(
   NAME = 
     c("Albemarle","Charlottesville"),
   county_projection_2040 = 
     c(138523,48939)) %>%
   left_join(
     dat %>%
       st_set_geometry(NULL) %>%
       group_by(NAME) %>%
       summarize(county_population_2020 = round(sum(total_population2020))))

countyPopulation_2040 %>%
  gather(Variable,Value, -NAME) %>%
  ggplot(aes(reorder(NAME,-Value),Value)) +
  geom_bar(aes(fill=Variable), stat = "identity", position = "dodge") +
  scale_fill_manual(values = palette2,
                    labels=c("2040","2020"),
                    name="Population") +
  labs(title="Population Change by County: 2020 - 2040",
       x="County", y="Population") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  plotTheme

5.2. Predicting Development Demand

Next, the countyPopulation_2040 table is joined to dat and pop_change in order to ‘distribute’ the new population across the study area. To do so, the the allocation of new population is weighted by a grid cell’s existing population (pop_2040.infill). 2020 population is subtracted from this figure to get pop_Change. Finally, Model6 is used to predict for 2040 given the updated population change and lag development features.

The map of predicted probabilities that results is best thought of as a measure of predicted development demand in 2040.

dat_infill <-
  dat %>%
  #calculate population change
    left_join(countyPopulation_2040) %>%
    mutate(proportion_of_county_pop = total_population2020 / county_population_2020,
           pop_2040.infill = proportion_of_county_pop * county_projection_2040,
           pop_Change = round(pop_2040.infill - total_population2020),2) %>%
    dplyr::select(-county_projection_2040, -county_population_2020, 
                  -proportion_of_county_pop, -pop_2040.infill) %>%
  #add values for 2020 baseline (forest, farm, etc.)
  
  #predict for 2040
    mutate(predict_2040.infill = predict(Model3,. , type="response"))

# dat_infill <- dat_infill %>% 
#   mutate(predict_2040.infill2 =ifelse(predict_2040.infill < 0.01 | predict_2040.infill > 1, 0, predict_2040.infill))

dat_infill %>%
  ggplot() +  
  geom_sf(aes(fill = predict_2040.infill), color = "transparent") +
  scale_fill_gradient(low = palette5[1],
                      high = palette5[length(palette5)],
                      name = "Stretched\nValues") +
  geom_sf(data = studyAreaCounties, fill = NA, colour = "black", size = 1) +
  labs(title = "Development Demand in 2040: Predicted Probabilities") +
  mapTheme

6. Comparing Predicted Development Demand & Environmental Sensitivity

We now have a really strong indicator of development demand for 2040 to help guide local land use planning. Demand however, is only one side of the equation. It must balanced with the supply of environmentally sensitive land. Understanding the interplay between demand and supply is the first stage of the ‘Allocation’ phase, where Planners ultimately decide which land should be developed and which should not.

For this analysis farmland and undeveloped land are be deemed Suitable, while environmentally sensitive areas like wetlands and forest are be deemed Not Suitable. Below, 2019 land cover data is read in and several measures of environmental sensitivity are created by county. These include:

  1. The total amount of wetlands and forest land cover area in 2019.
  2. The amount of sensitive land (wetland and forest) lost between 2001 and 2019.
  3. The total area of large sensitive landscape ‘patches’ in 2019.

The third metric warrants some further discussion. Leapfrog development is the idea that discontinuous development across space carves out disjointed patches of habitat. This fragmentation reduces biodiversity particularly for species that need room to roam. Below, environmentally sensitive_regions are created to represent large areas of un-fragmented habitat. We then calculate the total area of these clumps for each county.
## 6.1. 2019 Land Cover Data

To begin, the 2019 Land Cover data is read in and reclassified.

# Read raster data
lc_2019 <- raster("https://raw.githubusercontent.com/Yingtong-Z/sprawl-forecasting/cc191e7ec81e863672040f74745fbf9b8015b693/data/Cville_LC_2019.tif")

ggplot() +
  geom_raster(data = rbind(rast(lc_2001) %>% mutate(label = "2001"),
                           rast(lc_2019) %>% mutate(label = "2019")) %>% 
              na.omit %>% filter(value > 0), 
              aes(x,y,fill=as.factor(value))) +
  geom_sf(data = studyAreaCounties, fill = NA, colour = "red", size = 1) +
  facet_wrap(~label) +
  scale_fill_viridis(discrete=TRUE, name ="") +
  labs(title = "Land Cover, 2001 & 2019") +
  mapTheme + theme(legend.position = "none")

Next, each raster is aggregated to the fishnet using the aggregateRaster function and 2019 land cover types are mapped.

developed19 <- lc_2019 == 21 | lc_2019 == 22 | lc_2019 == 23 | lc_2019 == 24
forest19 <- lc_2019 == 41 | lc_2019 == 42 | lc_2019 == 43
farm19 <- lc_2019 == 81 | lc_2019 == 82
wetlands19 <- lc_2019 == 90 | lc_2019 == 95
otherUndeveloped19 <- lc_2019 == 52 | lc_2019 == 71 | lc_2019 == 31
water19 <- lc_2019 == 11

names(developed19) <- "developed19"
names(forest19) <- "forest19"
names(farm19) <- "farm19"
names(wetlands19) <- "wetlands19"
names(otherUndeveloped19) <- "otherUndeveloped19"
names(water19) <- "water19"

theRasterList19 <- c(developed19,forest19,farm19,wetlands19,otherUndeveloped19,water19)

###assign lc values based on hierarchy
dat2 <-
  aggregateRaster(theRasterList19, dat_infill) %>%
  dplyr::select(developed19,forest19,farm19,wetlands19,otherUndeveloped19,water19) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat) %>%
  st_sf() %>%
  st_cast("POLYGON")
# reassign cells with more than one lc value to just one based on hierarchy shown here
dat2 <- dat2 %>%
    mutate(farm19 = ifelse(developed19 == 0 & farm19 == 1, 1, 0)) %>%
    mutate(forest19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 1, 1, 0)) %>%
    mutate(wetlands19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 0 & wetlands19 == 1, 1, 0)) %>%
    mutate(water19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 0 & wetlands19 == 0 & water19 == 1, 1, 0)) %>%
    mutate(otherUndeveloped19 = ifelse(developed19 == 0 & farm19 == 0 & forest19 == 0 & wetlands19 == 0 & water19 == 0 & otherUndeveloped19 == 1, 1, 0))


dat2 %>%
  gather(var,value,developed19:water19) %>%
  st_centroid() %>%
  mutate(X = st_coordinates(.)[,1],
         Y = st_coordinates(.)[,2]) %>%
  ggplot() +
    geom_sf(data=CvilleMSA) +
    geom_point(aes(X,Y, colour=as.factor(value)), size = 0.1) +
    facet_wrap(~var) +
    scale_colour_manual(values = palette2,
                        labels=c("Other","Land Cover"),
                        name = "") +
    labs(title = "Land Cover Types, 2019",
         subtitle = "As fishnet centroids") +
   mapTheme

Next, we reformat 2019 Land Cover for our Model, preserving 2001 features as new columns.

dat2 <- dat2 %>%
# 2001 land cover
    mutate(farm01 = farm) %>%
    mutate(forest01 = forest) %>%
    mutate(wetlands01 = wetlands) %>%
    mutate(water01 = water) %>%
    mutate(otherUndeveloped01 = otherUndeveloped) %>%
    mutate(total_housing_units01 = total_housing_units) %>%
    mutate(total_white01 = total_white) %>% 
# 2019 land cover
    mutate(farm = farm19) %>%
    mutate(forest = forest19) %>%
    mutate(wetlands = wetlands19) %>%
    mutate(water = water19) %>%
    mutate(otherUndeveloped = otherUndeveloped19) %>%
    mutate(total_housing_units = total_housing_units2020) %>%
    mutate(total_white = total_white2020)

6.2. Sensitive Land Cover Lost

Below an indicator sensitive_lost is created indicating grid cells that were either forest or wetlands in 2001 but were no longer so in 2019. The output layer, sensitive_land_lost, gives a sense for how development in the recent past has effected the natural environment.

dat2 <-
  dat2 %>%
   mutate(sensitive_lost19 = ifelse(forest01 == 1 & forest19 == 0 |
                                    wetlands01 == 1 & wetlands19 == 0,1,0))
                      
ggplot() +
  geom_sf(data=dat2, aes(fill =as.factor(sensitive_lost19)), color = "transparent") +
  scale_fill_manual(values = palette2,
                      labels=c("No Change","Sensitive Lost"),
                      name = "") +
  labs(title = "Sensitive lands lost: 2001 - 2019") +
  mapTheme

6.3 Landscape Fragmentation

In this section, the wetlands19 and forest19 rasters are converted to contiguous sensitive_regions using the raster::clump function. This is equivalent to Region Group in ArcGIS. The raster clumps are then converted to vector sf layers; dissolved into unique regions; Acres are calculated; and the layers are converted back to raster to be extracted back to the fishnet with aggregateRaster. Note that only sensitive_regions with areas greater than 1 acre are included.

sensitiveRegions <- 
  raster::clump(wetlands19 + forest19) %>%
  rasterToPolygons() %>%
  st_as_sf() %>%
  group_by(clumps) %>% 
  summarize() %>%
    mutate(Acres = as.numeric(st_area(.) * 0.0000229568)) %>%
    filter(Acres > 3954)  %>%
  dplyr::select() %>%
  raster::rasterize(.,emptyRaster) 
sensitiveRegions[sensitiveRegions > 0] <- 1  
names(sensitiveRegions) <- "sensitiveRegions"

dat2 <-
  aggregateRaster(c(sensitiveRegions), dat2) %>%
  dplyr::select(sensitiveRegions) %>%
  st_set_geometry(NULL) %>%
  bind_cols(.,dat2) %>%
  st_sf()

ggplot() +
    geom_sf(data=dat2, aes(fill =as.factor(sensitiveRegions)), color = "transparent") +
  scale_fill_manual(values = palette2,
                      labels=c("Other","Sensitive Regions"),
                      name="") +
  labs(title = "Sensitive regions",
       subtitle = "Continous areas of either wetlands or forests\ngreater than 1 acre") +
  mapTheme

6.4. Summarize by County

The below dplyr statement takes as its input, dat2, which was created in Sections 6.2 - 6.4 and wrangles together a table of county-level, supply and demand metrics which can be used to analyze suitability by county.

county_specific_metrics <- 
  dat2 %>%
  #predict development demand from our model
  mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
  #get a count count of grid cells by county which we can use to calculate rates below
  left_join(st_set_geometry(dat, NULL) %>% group_by(NAME) %>% summarize(count = n())) %>%
  #calculate summary statistics by county
  group_by(NAME) %>%
  summarize(Total_Farmland = sum(farm19) / max(count),
            Total_Forest = sum(forest19) / max(count),
            Total_Wetlands = sum(wetlands19) / max(count),
            Total_Undeveloped = sum(otherUndeveloped19) / max(count),
            Sensitive_Land_Lost = sum(sensitive_lost19) / max(count),
            Sensitive_Regions = sum(sensitiveRegions) / max(count),
            Mean_Development_Demand = mean(Development_Demand)) %>%
  #get population data by county
  left_join(countyPopulation_2040 %>% 
            mutate(Population_Change = county_projection_2040 - county_population_2020,
                   Population_Change_Rate = Population_Change / county_projection_2040) %>%
            dplyr::select(NAME,Population_Change_Rate))

Now a small multiple plot can be created providing both supply and demand side analytics by county. The plot gives a sense for development demand (Demand-Side), suitable land for development (Suitable) and sensitive land (Not Suitable).

The data suggests both population and development demand will increase for Charlottesville MSA. However, compared to Charlottesville county, Albemarle has a high rate of developable farmland and a low supply of sensitive land. This suggests Albemarle is more suitable to new development than Charlottesville, which is highly developed.

county_specific_metrics %>%
  gather(Variable, Value, -NAME, -geometry) %>%
  mutate(Variable = factor(Variable, levels=c("Population_Change_Rate","Mean_Development_Demand",
                                              "Total_Farmland","Total_Undeveloped","Total_Forest",
                                              "Total_Wetlands","Sensitive_Land_Lost","Sensitive_Regions",
                                              ordered = TRUE))) %>%
  mutate(Planning_Designation = case_when(
    Variable == "Population_Change_Rate" | Variable == "Mean_Development_Demand" ~ "Demand-Side",
    Variable == "Total_Farmland" | Variable == "Total_Undeveloped"               ~ "Suitable",
    TRUE                                                                         ~ "Not Suitable")) %>%
  ggplot(aes(x=Variable, y=Value, fill=Planning_Designation)) +
    geom_bar(stat="identity", position=position_dodge(), colour="black") +
    facet_wrap(~NAME, ncol=5) +
    coord_flip() +
    scale_y_continuous(breaks = seq(.25, 1, by = .25)) +
    geom_vline(xintercept = 2.5) + geom_vline(xintercept = 4.5) +
    scale_fill_manual(values=c("black","red","darkgreen")) +
    labs(title= "County Specific Allocation Metrics", subtitle= "As rates", x="Indicator", y="Rate") +
    plotTheme + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="bottom")

7. Allocation

Allocation is the final stage of the urban growth modeling process. Now that both demand and supply is understood, We can allocate development rights accordingly. Of course, this could take many forms of regulation including zoning, subdivision approval or outright conservation. In this section, demand and supply are visualized for two counties, Charlottesville and Albemarle. The data suggests that the latter is more conducive to growth while the former is less so.

First, development demand is predicted for Albemarle. Then a layer, Albemarle_landUse is created, that includes indicators for both previously developed land and environmentally unsuitable land. This layer then is overlayed atop development demand and projected population change to give the full supply and demand-side picture in Albemarle.

There are some clear opportunities for development in Albemarle. Significant infill opportunities exist along the roads and highways where population change is projected to be greatest. There is also a good deal of environmentally suitable land along the highways. This would be ideal space for land developments.

Albemarle <-
  dat2 %>%   #calculate population change
    left_join(countyPopulation_2040) %>%
    mutate(proportion_of_county_pop = total_population2020 / county_population_2020,
           pop_2040.infill = proportion_of_county_pop * county_projection_2040,
           pop_Change = (pop_2040.infill - total_population2020)) %>%
    dplyr::select(-county_projection_2040, -county_population_2020, 
                  -proportion_of_county_pop, -pop_2040.infill) %>%
    mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
    filter(NAME == "Albemarle") 


Albemarle_landUse <- rbind(
  filter(Albemarle, forest19 == 1 | wetlands19 == 1 ) %>%
  dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
  filter(Albemarle, developed19 == 1) %>%
  dplyr::select() %>% mutate(Land_Use = "Developed"))

grid.arrange(
ggplot() +
  geom_sf(data=Albemarle, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
  geom_point(data=Albemarle_landUse, aes(x=xyC(Albemarle_landUse)[,1],
                                        y=xyC(Albemarle_landUse)[,2], colour=Land_Use),
                                        shape = 16, size = 0.25) +
  geom_sf(data=st_intersection(CvilleHighways,filter(studyAreaCounties, NAME=="Albemarle")), size=2) +
  scale_fill_manual(values = palette5, name="Development_Demand",
                    labels=substr(quintileBreaks(Albemarle,"Development_Demand"),1,5)) +
  scale_colour_manual(values = c("black","red")) +
  labs(title = "Development Potential, \n2040: Albemarle") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),

ggplot() +
  geom_sf(data=Albemarle, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
  geom_point(data=Albemarle_landUse, aes(x=xyC(Albemarle_landUse)[,1],
                                        y=xyC(Albemarle_landUse)[,2], colour=Land_Use),
                                        shape = 16, size = 0.25) +
  geom_sf(data=st_intersection(CvilleHighways,filter(studyAreaCounties, NAME=="Albemarle")), size=2) +
  scale_fill_manual(values = palette5, name="Population_Change",
                    labels=substr(quintileBreaks(Albemarle,"pop_Change"),1,5)) +
  scale_colour_manual(values = c("black","red")) +
  labs(title = "Projected Population, \n2040: Albemarle") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

The plots above are created using a ggplot trick to show our fishnet grid cells overlayed by color-coded points.

For comparison purposes, this process is replicated for Charlottesville county below.

Charlottesville <-
  dat2 %>%   #calculate population change
    left_join(countyPopulation_2040) %>%
    mutate(proportion_of_county_pop = total_population2020 / county_population_2020,
           pop_2040.infill = proportion_of_county_pop * county_projection_2040,
           pop_Change = (pop_2040.infill - total_population2020)) %>%
    dplyr::select(-county_projection_2040, -county_population_2020, 
                  -proportion_of_county_pop, -pop_2040.infill) %>%
    mutate(Development_Demand = predict(Model6, dat2, type="response")) %>%
    filter(NAME == "Charlottesville") 


Charlottesville_landUse <- rbind(
  filter(Charlottesville, forest19 == 1 | wetlands19 == 1 ) %>%
  dplyr::select() %>% mutate(Land_Use = "Not Suitable"),
  filter(Charlottesville, developed19 == 1) %>%
  dplyr::select() %>% mutate(Land_Use = "Developed"))

grid.arrange(
ggplot() +
  geom_sf(data=Charlottesville, aes(fill=factor(ntile(Development_Demand,5))), colour=NA) +
  geom_point(data=Charlottesville_landUse, aes(x=xyC(Charlottesville_landUse)[,1],
                                        y=xyC(Charlottesville_landUse)[,2], colour=Land_Use),
                                        shape = 16, size = 1) +
  scale_fill_manual(values = palette5, name="Development_Demand",
                    labels=substr(quintileBreaks(Charlottesville,"Development_Demand"),1,5)) +
  scale_colour_manual(values = c("black","red")) +
  labs(title = "Development Potential, \n2040: Charlottesville") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)),

ggplot() +
  geom_sf(data=Charlottesville, aes(fill=factor(ntile(pop_Change,5))), colour=NA) +
  geom_point(data=Charlottesville_landUse, aes(x=xyC(Charlottesville_landUse)[,1],
                                        y=xyC(Charlottesville_landUse)[,2], colour=Land_Use),
                                        shape = 16, size = 1) +
  scale_fill_manual(values = palette5, name="Population_Change",
                    labels=substr(quintileBreaks(Albemarle,"pop_Change"),1,5)) +
  scale_colour_manual(values = c("black","red")) +
  labs(title = "Projected Population, \n2040: Charlottesville") + mapTheme +
  guides(fill = guide_legend(order = 1), colour = guide_legend(order = 2)), ncol=2)

8. Conclusion

We stop short of actually allocating land to development. While our model is well-suited for understanding sprawl-style development, it is not useful for understanding how new demand might be absorbed by upzoning and densification of existing development. It would not be wise to allocate the entire projected population to undeveloped land. Instead, we’d prefer a more nuanced understanding of how local land use laws might play a role. At this stage in the analysis however, the Planner has all they need to engage local stakeholders about future development decisions.

Next, we ran the same script with a new highways dataset upgrading an existing road connecting Charlottesville and Crozet to a highway to see how this new infrastructure might impact urban growth projections. Here are those results for Albemarle:

Albemarle County

And here are the results for Charlottesville:

Charlottesville County

Html for this script is linked here

LS0tCnRpdGxlOiAiQ2hhcmxvdHRlc3ZpbGxlIE1TQSBVcmJhbiBHcm93dGggTW9kZWxpbmciCmF1dGhvcjogIk9saXZlciBBdHdvb2QsIFlpbmd0b25nIFpob25nLCBhZGFwdGVkIGZyb20gS2VuIFN0ZWlmLCAyMDE5LiBFZHMuIE1pY2hhZWwgRmljaG1hbiAmIEplbm5hIEVwc3RlaW4iCmRhdGU6ICIwNS8wMi8yMDIzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKCnJtKGxpc3Q9bHMoKSkKYGBgCgo8c3R5bGU+CiAgLnN1cGVyYmlnaW1hZ2V7CiAgICAgIG92ZXJmbG93LXg6c2Nyb2xsOwogICAgICB3aGl0ZS1zcGFjZTogbm93cmFwOwogIH0KCiAgLnN1cGVyYmlnaW1hZ2UgaW1newogICAgIG1heC13aWR0aDogbm9uZTsKICB9CgoKPC9zdHlsZT4KCgoKIyAxLiBJbnRyb2R1Y3Rpb24KUmVnaW9uYWwgdXJiYW4gZGV2ZWxvcG1lbnQgaXMgaW5mbHVlbmNlZCBieSB2YXJpb3VzIHN0YWtlaG9sZGVycyBzdWNoIGFzIGRldmVsb3BlcnMsIHJlYWwgZXN0YXRlIGJ1eWVycywgdGVuYW50cywgcGxhbm5lcnMsIGFuZCByZWd1bGF0b3JzLCBlYWNoIHB1cnN1aW5nIHRoZWlyIG93biBvYmplY3RpdmVzLiBUbyBlbnN1cmUgZWNvbm9taWMgcHJvZHVjdGl2aXR5IGFuZCBzdXN0YWluYWJpbGl0eSwgbGFuZCB1c2UgcGxhbm5pbmcgbXVzdCBjb25zaWRlciBib3RoIHN1cHBseSBhbmQgZGVtYW5kLXNpZGUgaW5zaWdodHMuCgpUaGlzIHByb2plY3QgZm9jdXNlcyBvbiB0aGUgQ2hhcmxvdHRlc3ZpbGxlIE1ldHJvcG9saXRhbiBTdGF0aXN0aWNhbCBBcmVhIChNU0EpIGFzIGEgY2FzZSBzdHVkeSwgZXhhbWluaW5nIGhvdyBhIHNwcmF3bGluZyBtZXRyb3BvbGl0YW4gYXJlYSBjYW4gYmFsYW5jZSBlY29ub21pYyBncm93dGggd2l0aCBlbnZpcm9ubWVudGFsIHN1c3RhaW5hYmlsaXR5IGJ5IHByb2plY3RpbmcgbGFuZCBjb3ZlciBjaGFuZ2UuIFRoZSBwcm9qZWN0IGRyYXdzIG9uIGRhdGEgZnJvbSBzb3VyY2VzIHN1Y2ggYXMgdGhlIFVTIEdlb2xvZ2ljYWwgU3VydmV5IChVU0dTKSwgQ2Vuc3VzIGRlbW9ncmFwaGljcywgYW5kIE9wZW4gU3RyZWV0IE1hcHMgdHJhbnNwb3J0YXRpb24gaW5mcmFzdHJ1Y3R1cmUsIGFzIHdlbGwgYXMgc3BhdGlhbCBsYWcgZmVhdHVyZXMgZGVyaXZlZCBmcm9tIGxhbmQgY292ZXIgY2hhbmdlLCB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGV2ZWxvcG1lbnQgZGVtYW5kIGFuZCBlbnZpcm9ubWVudGFsbHkgc2Vuc2l0aXZlIGxhbmQuCgpUaHJvdWdoIGV4cGxvcmF0b3J5IGFuYWx5c2lzLCBhIGdlb3NwYXRpYWwgcHJlZGljdGl2ZSBtb2RlbCBpcyBkZXZlbG9wZWQsIHRyYWluZWQgb24gbGFuZCBjb3ZlciBjaGFuZ2VzIGZyb20gMjAwMS0yMDE5LiBUaGlzIG1vZGVsIGlzIHVzZWQgdG8gZXN0aW1hdGUgZGV2ZWxvcG1lbnQgZGVtYW5kIGZvciB0aGUgeWVhciAyMDQwLCB3aGlsZSBjb25zaWRlcmluZyB0aGUgaW1wYWN0IG9uIHRoZSBlbnZpcm9ubWVudCBhbmQgbGFuZHNjYXBlIGZyYWdtZW50YXRpb24uIFRoZSBtb2RlbCdzIHByZWRpY3Rpb25zIGFyZSB0aGVuIHVzZWQgdG8gZ3VpZGUgbmV3IGRldmVsb3BtZW50IGluIGFyZWFzIHRoYXQgc3VwcG9ydCBlY29ub21pYyBncm93dGggd2l0aG91dCBjb21wcm9taXNpbmcgc3VzdGFpbmFiaWxpdHkgZ29hbHMuCgojIDEuMi4gU2V0dXAKCkJlbG93IHdlIGxvYWQgcmVxdWlyZWQgbGlicmFyaWVzLCBtYXBUaGVtZSBhbmQgcGxvdFRoZW1lIGZvciBjb25zaXN0ZW50IHN0eWxpbmcsIGFuZCBzcGVjaWZ5IGEgcGFsZXR0ZSBvZiBjb2xvcnMgZm9yIHZpc3VhbGl6YXRpb25zCgpgYGB7ciBsb2FkX3BhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzID0gImhpZGUifQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc2YpCmxpYnJhcnkocmFzdGVyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkodGlkeWNlbnN1cykKbGlicmFyeSh0aWdyaXMpCmxpYnJhcnkoRk5OKQojbGlicmFyeShRdWFudFBzeWMpICMgSkUgTm90ZTogaW4gUiA0LjEsIFF1YW50UHN5YyBwYWNrYWdlIG5vdCBhdmFpbGFibGUuCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoeWFyZHN0aWNrKQpsaWJyYXJ5KHBzY2wpCmxpYnJhcnkocGxvdFJPQykgCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwUk9DKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KG1hcHZpZXcpCmxpYnJhcnkoZXhhY3RleHRyYWN0cikKCgpwbG90VGhlbWUgPC0gdGhlbWUoCiAgcGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICMgU2V0IHRoZSBlbnRpcmUgY2hhcnQgcmVnaW9uIHRvIGJsYW5rCiAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgcGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICAjcGFuZWwuYm9yZGVyPWVsZW1lbnRfcmVjdChjb2xvdXI9IiNGMEYwRjAiKSwKICAjIEZvcm1hdCB0aGUgZ3JpZAogIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91cj0iI0QwRDBEMCIsc2l6ZT0uNzUpLAogIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpKQoKbWFwVGhlbWUgPC0gdGhlbWUocGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICAgICAgICAgIGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91ciA9ICd0cmFuc3BhcmVudCcpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIAogICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAxLCAnY20nKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDEsICJjbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjIsICJjbSIpKQoKcGFsZXR0ZTEgPC0gYygiI2NkMDAwMCIsIiMwMDU5YzciKQpwYWxldHRlMiA8LSBjKCIjNDFiNmM0IiwiIzI1MzQ5NCIpCnBhbGV0dGU0IDwtIGMoIiNhMWRhYjQiLCIjNDFiNmM0IiwiIzJjN2ZiOCIsIiMyNTM0OTQiKQpwYWxldHRlNSA8LSBjKCIjZmZmZmNjIiwiI2ExZGFiNCIsIiM0MWI2YzQiLCIjMmM3ZmI4IiwiIzI1MzQ5NCIpCnBhbGV0dGUxMCA8LSBjKCIjZjdmY2YwIiwiI2UwZjNkYiIsIiNjY2ViYzUiLCIjYThkZGI1IiwiIzdiY2NjNCIsCiAgICAgICAgICAgICAgICIjNGViM2QzIiwiIzJiOGNiZSIsIiMwODY4YWMiLCIjMDg0MDgxIiwiI2Y3ZmNmMCIpCmBgYAoKV2UgYWxzbyBpbmNsdWRlIHNldmVyYWwgaGVscGVyIGZ1bmN0aW9ucy4gYHF1aW50aWxlc0JyZWFrc2AgdGFrZXMgYSBkYXRhZnJhbWUgYW5kIGEgY29sdW1uIGFuZCBvdXRwdXRzIHRoZSBxdWludGlsZXMgYnJlYWtzLCBoZWxwaW5nIHNob3J0ZW4gdGhlIGBnZ3Bsb3RgIGNhbGxzIGJlbG93LgoKSXQgdGFrZXMgbG9uZ2VyIHRvIGBnZ3Bsb3RgIGEgcG9seWdvbiBmaXNobmV0IHdpdGggYGdlb21fc2ZgIHRoYW4gaXQgZG9lcyB0byBwbG90IGBnZW9tX3BvaW50YC4gVG8gY3V0IGRvd24gb24gcGxvdHRpbmcgdGltZSwgdGhlIGB4eUNgIChmb3Ig4oCYWFkgQ29vcmRpbmF0ZXPigJkpIHRha2VzIGEgZmlzaG5ldCBgc2ZgIGFuZCBjb252ZXJ0cyBpdCB0byBhIGRhdGFmcmFtZSBvZiBncmlkIGNlbGwgY2VudHJvaWQgY29vcmRpbmF0ZXMuCgpgcmFzdGAgaXMgYSBmdW5jdGlvbiBhbGxvd2luZyB1cyB0byBxdWlja2x5IHBsb3QgcmFzdGVyIHZhbHVlcyBpbiBgZ2dwbG90YC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KI3RoaXMgZnVuY3Rpb24gY29udmVydHMgYSBjb2x1bW4gaW4gdG8gcXVpbnRpbGVzLiBJdCBpcyB1c2VkIGZvciBtYXBwaW5nLgpxdWludGlsZUJyZWFrcyA8LSBmdW5jdGlvbihkZix2YXJpYWJsZSkgewogICAgYXMuY2hhcmFjdGVyKHF1YW50aWxlKGRmW1t2YXJpYWJsZV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgIGMoLjAxLC4yLC40LC42LC44KSxuYS5ybT1UKSkKfQoKI1RoaXMgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gY29udmVydCBhIHBvbHlnb24gc2YgdG8gY2VudHJvaWRzIHh5IGNvb3Jkcy4KeHlDIDwtIGZ1bmN0aW9uKGFQb2x5Z29uU0YpIHsKICBhcy5kYXRhLmZyYW1lKAogICAgY2JpbmQoeD1zdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChhUG9seWdvblNGKSlbLDFdLAogICAgICAgICAgeT1zdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChhUG9seWdvblNGKSlbLDJdKSkKfSAKCiN0aGlzIGZ1bmN0aW9uIGNvbnZlcnQgYSByYXN0ZXIgdG8gYSBkYXRhIGZyYW1lIHNvIGl0IGNhbiBiZSBwbG90dGVkIGluIGdncGxvdApyYXN0IDwtIGZ1bmN0aW9uKGluUmFzdGVyKSB7CiAgZGF0YS5mcmFtZSgKICAgIHh5RnJvbUNlbGwoaW5SYXN0ZXIsIDE6bmNlbGwoaW5SYXN0ZXIpKSwgCiAgICB2YWx1ZSA9IGdldFZhbHVlcyhpblJhc3RlcikpIH0KYGBgCgoKIyAyLiBEYXRhIFdyYW5nbGluZyAmIEZlYXR1cmUgRW5naW5lZXJpbmcKCkluIHRoaXMgc2VjdGlvbiBhIGNvbnNpZGVyYWJsZSBhbW91bnQgb2YgdmVjdG9yIGFuZCByYXN0ZXIgZGF0YSBpcyB3cmFuZ2xlZCB0b2dldGhlciBpbnRvIGEgcmVncmVzc2lvbi1yZWFkeSBkYXRhc2V0LiBUaGUgZm9sbG93aW5nIGRhdGFzZXRzIGFyZSB1c2VkOgoKMi4xIC0gMi4yOiBMYW5kIGNvdmVyIGRhdGEgW2Rvd25sb2FkZWRdKGh0dHBzOi8vd3d3Lm1ybGMuZ292L2RhdGEvbmxjZC1sYW5kLWNvdmVyLWNoYW5nZS1pbmRleC1jb251cykgZnJvbSB0aGUgTXVsdGktUmVzb2x1dGlvbiBMYW5kIENoYXJhY3RlcmlzdGljcyBDb25zb3J0aXVt4oCZcyBOYXRpb25hbCBMYW5kIENvdmVyIERhdGFiYXNlIChOTENEKSBpbmNsdWRlcyBhbm51YWwgbGFuZCBjb3ZlciBhbmQgbGFuZCBjb3ZlciBjaGFuZ2UgcmFzdGVyIGRhdGEgZm9yIHRoZSBDaGFybG90dGVzdmlsbGUgYW5kIEFsYmVtYXJsZSBjb3VudHJpZXMuIFRoZXNlIGRhdGEgYXJlIHNhbXBsZWQgdG8gYSA0LDAwMCBieSA0LDAwMCBmdF4yIGZpc2huZXQuCgoyLjM6IFBvcHVsYXRpb24gZGF0YSBpcyBkb3dubG9hZGVkIGZyb20gdGhlIFUuUy4gQ2Vuc3VzIGFuZCBqb2luZWQgdG8gdGhlIGZpc2huZXQgYnkgZGlzdHJpYnV0aW5nIEJsb2NrIHBvcHVsYXRpb24gdG90YWxzIHByb3BvcnRpb25hbGx5IHRvIGVhY2ggZ3JpZCBjZWxsLgoKMi40OiBIaWdod2F5IHZlY3RvcnMgYXJlIGRvd25sb2FkZWQgZnJvbSBPcGVuIFN0cmVldCBNYXAgZGF0YSB2aWEgR2VvRmFicmlrLmRlLCBjbGVhbmVkIGluIEFyY0dJUyBQcm8sIGFuZCBleHBvcnRlZCBmb3IgcHJveGltaXR5IHRvIGhpZ2h3YXkgYW5hbHlzaXMgaW4gUi4KCjIuNTogU2xvcGUgdmFsdWVzIGFyZSBkZXJpdmVkIGZyb20gMTIuNW0gcmVzb2x1dGlvbiBERU0gaW4gQXJjR0lTIFBybyBhbmQgaW1wb3J0ZWQgdG8gUiBmb3IgaW5jbHVzaW9uIGluIG1vZGVscy4KCjIuNjogTGFuZCBjb3ZlciBjaGFuZ2UgZGF0YSBmcm9tIE5MQ0QgaXMgdXNlZCB0byBlbmdpbmVlciBzcGF0aWFsIGxhZyBmZWF0dXJlcy4KCjIuNzogQ291bnR5IHBvbHlnb25zIGFyZSBkb3dubG9hZGVkIHVzaW5nIHRoZSBgdGlncmlzYCBwYWNrYWdlLgoKMi44OiBGZWF0dXJlcyBhcmUgd3JhbmdsZWQgaW50byBhIGZpbmFsIGRhdGFzZXQuCgpEYXRhIGZyb20gZWFjaCBvZiB0aGVzZSBkYXRhc2V0cyBpcyBpbnRlZ3JhdGVkIGludG8gdGhlIHZlY3RvciBmaXNobmV0IHRvIHByb3ZpZGUgYSBjb21wcmVoZW5zaXZlIHNuYXBzaG90IG9mIHRoZSBkZXZlbG9wbWVudCBwcm9jZXNzIGFuZCBjb250ZXh0IGluIGFuZCBhcm91bmQgQ2hhcmxvdHRlc3ZpbGxlIGJldHdlZW4gMjAwMSBhbmQgMjAxOS4KPGJyPgoKIyMgMi4xLiBMYW5kIENvdmVyIENoYW5nZSBEYXRhCgpXZSBhaW0gdG8gZm9yZWNhc3QgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBvZiBsYW5kIGNvdmVyIGNoYW5nZSBiZXR3ZWVuIDIwMDEgYW5kIDIwMTkuIEluIHRoaXMgc2VjdGlvbiwgd2UgbG9hZCB0aGUgbGFuZCBjb3ZlciByYXN0ZXIgZGF0YSwgcmVjbGFzc2lmeSBpdCwgYW5kIGludGVncmF0ZSBpdCB3aXRoIGEgdmVjdG9yIGZpc2huZXQuIFRoaXMgYWxsb3dzIHVzIHRvIHBhcmFtZXRlcml6ZSBzcGF0aWFsIHJlbGF0aW9uc2hpcHMgaW4gYSByZWdyZXNzaW9uIGNvbnRleHQuCgpUaGUgdGFibGUgYmVsb3cgc2hvd3MgZGVzY3JpcHRpb25zIG9mIGVhY2ggY2F0ZWdvcmljYWwgbGFuZCBjb3ZlciB0eXBlIGluIHRoZSBsYW5kIGNvdmVyIGRhdGEuIEJlbG93LCB3ZSB3aWxsIHJlY2xhc3NpZnkgdGhlc2UgZGF0YSBpbnRvIG1vcmUgdXNlZnVsIGNhdGVnb3JpZXMuCgp8IENsYXNzIFZhbHVlIHwgQ2xhc3NpZmljYXRpb24gRGVzY3JpcHRpb24gfAp8IC0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfAp8IFdhdGVyICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDExICAgICAgICAgIHwgT3BlbiBXYXRlciAtIGFyZWFzIG9mIG9wZW4gd2F0ZXIsIGdlbmVyYWxseSB3aXRoIGxlc3MgdGhhbiAyNSUgY292ZXIgb2YgdmVnZXRhdGlvbiBvciBzb2lsLiB8CnwgMTIgICAgICAgICAgfCBQZXJlbm5pYWwgSWNlL1Nub3cgLSBhcmVhcyBjaGFyYWN0ZXJpemVkIGJ5IGEgcGVyZW5uaWFsIGNvdmVyIG9mIGljZSBhbmQvb3Igc25vdywgZ2VuZXJhbGx5IGdyZWF0ZXIgdGhhbiAyNSUgb2YgdG90YWwgY292ZXIuIHwKfCBEZXZlbG9wZWQgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAyMSAgICAgICAgICB8IERldmVsb3BlZCwgT3BlbiBTcGFjZSAtIGFyZWFzIHdpdGggYSBtaXh0dXJlIG9mIHNvbWUgY29uc3RydWN0ZWQgbWF0ZXJpYWxzLCBidXQgbW9zdGx5IHZlZ2V0YXRpb24gaW4gdGhlIGZvcm0gb2YgbGF3biBncmFzc2VzLiBJbXBlcnZpb3VzIHN1cmZhY2VzIGFjY291bnQgZm9yIGxlc3MgdGhhbiAyMCUgb2YgdG90YWwgY292ZXIuIFRoZXNlIGFyZWFzIG1vc3QgY29tbW9ubHkgaW5jbHVkZSBsYXJnZS1sb3Qgc2luZ2xlLWZhbWlseSBob3VzaW5nIHVuaXRzLCBwYXJrcywgZ29sZiBjb3Vyc2VzLCBhbmQgdmVnZXRhdGlvbiBwbGFudGVkIGluIGRldmVsb3BlZCBzZXR0aW5ncyBmb3IgcmVjcmVhdGlvbiwgZXJvc2lvbiBjb250cm9sLCBvciBhZXN0aGV0aWMgcHVycG9zZXMuIHwKfCAyMiAgICAgICAgICB8IERldmVsb3BlZCwgTG93IEludGVuc2l0eSAtIGFyZWFzIHdpdGggYSBtaXh0dXJlIG9mIGNvbnN0cnVjdGVkIG1hdGVyaWFscyBhbmQgdmVnZXRhdGlvbi4gSW1wZXJ2aW91cyBzdXJmYWNlcyBhY2NvdW50IGZvciAyMCUgdG8gNDklIHBlcmNlbnQgb2YgdG90YWwgY292ZXIuIFRoZXNlIGFyZWFzIG1vc3QgY29tbW9ubHkgaW5jbHVkZSBzaW5nbGUtZmFtaWx5IGhvdXNpbmcgdW5pdHMuIHwKfCAyMyAgICAgICAgICB8IERldmVsb3BlZCwgTWVkaXVtIEludGVuc2l0eSAtIGFyZWFzIHdpdGggYSBtaXh0dXJlIG9mIGNvbnN0cnVjdGVkIG1hdGVyaWFscyBhbmQgdmVnZXRhdGlvbi4gSW1wZXJ2aW91cyBzdXJmYWNlcyBhY2NvdW50IGZvciA1MCUgdG8gNzklIG9mIHRoZSB0b3RhbCBjb3Zlci4gVGhlc2UgYXJlYXMgbW9zdCBjb21tb25seSBpbmNsdWRlIHNpbmdsZS1mYW1pbHkgaG91c2luZyB1bml0cy4gfAp8IDI0ICAgICAgICAgIHwgRGV2ZWxvcGVkIEhpZ2ggSW50ZW5zaXR5IC0gaGlnaGx5IGRldmVsb3BlZCBhcmVhcyB3aGVyZSBwZW9wbGUgcmVzaWRlIG9yIHdvcmsgaW4gaGlnaCBudW1iZXJzLiBFeGFtcGxlcyBpbmNsdWRlIGFwYXJ0bWVudCBjb21wbGV4ZXMsIHJvdyBob3VzZXMgYW5kIGNvbW1lcmNpYWwvaW5kdXN0cmlhbC4gSW1wZXJ2aW91cyBzdXJmYWNlcyBhY2NvdW50IGZvciA4MCUgdG8gMTAwJSBvZiB0aGUgdG90YWwgY292ZXIuIHwKfCBCYXJyZW4gICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAzMSAgICAgICAgICB8IEJhcnJlbiBMYW5kIChSb2NrL1NhbmQvQ2xheSkgLSBhcmVhcyBvZiBiZWRyb2NrLCBkZXNlcnQgcGF2ZW1lbnQsIHNjYXJwcywgdGFsdXMsIHNsaWRlcywgdm9sY2FuaWMgbWF0ZXJpYWwsIGdsYWNpYWwgZGVicmlzLCBzYW5kIGR1bmVzLCBzdHJpcCBtaW5lcywgZ3JhdmVsIHBpdHMgYW5kIG90aGVyIGFjY3VtdWxhdGlvbnMgb2YgZWFydGhlbiBtYXRlcmlhbC4gR2VuZXJhbGx5LCB2ZWdldGF0aW9uIGFjY291bnRzIGZvciBsZXNzIHRoYW4gMTUlIG9mIHRvdGFsIGNvdmVyLiB8CnwgRm9yZXN0ICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgNDEgICAgICAgICAgfCBEZWNpZHVvdXMgRm9yZXN0IC0gYXJlYXMgZG9taW5hdGVkIGJ5IHRyZWVzIGdlbmVyYWxseSBncmVhdGVyIHRoYW4gNSBtZXRlcnMgdGFsbCwgYW5kIGdyZWF0ZXIgdGhhbiAyMCUgb2YgdG90YWwgdmVnZXRhdGlvbiBjb3Zlci4gTW9yZSB0aGFuIDc1JSBvZiB0aGUgdHJlZSBzcGVjaWVzIHNoZWQgZm9saWFnZSBzaW11bHRhbmVvdXNseSBpbiByZXNwb25zZSB0byBzZWFzb25hbCBjaGFuZ2UuIHwKfCA0MiAgICAgICAgICB8IEV2ZXJncmVlbiBGb3Jlc3QgLSBhcmVhcyBkb21pbmF0ZWQgYnkgdHJlZXMgZ2VuZXJhbGx5IGdyZWF0ZXIgdGhhbiA1IG1ldGVycyB0YWxsLCBhbmQgZ3JlYXRlciB0aGFuIDIwJSBvZiB0b3RhbCB2ZWdldGF0aW9uIGNvdmVyLiBNb3JlIHRoYW4gNzUlIG9mIHRoZSB0cmVlIHNwZWNpZXMgbWFpbnRhaW4gdGhlaXIgbGVhdmVzIGFsbCB5ZWFyLiBDYW5vcHkgaXMgbmV2ZXIgd2l0aG91dCBncmVlbiBmb2xpYWdlLiB8CnwgNDMgICAgICAgICAgfCBNaXhlZCBGb3Jlc3QgLSBhcmVhcyBkb21pbmF0ZWQgYnkgdHJlZXMgZ2VuZXJhbGx5IGdyZWF0ZXIgdGhhbiA1IG1ldGVycyB0YWxsLCBhbmQgZ3JlYXRlciB0aGFuIDIwJSBvZiB0b3RhbCB2ZWdldGF0aW9uIGNvdmVyLiBOZWl0aGVyIGRlY2lkdW91cyBub3IgZXZlcmdyZWVuIHNwZWNpZXMgYXJlIGdyZWF0ZXIgdGhhbiA3NSUgb2YgdG90YWwgdHJlZSBjb3Zlci4gfAp8IFNocnVibGFuZCAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IDUxICAgICAgICAgIHwgRHdhcmYgU2NydWIgLSBBbGFza2Egb25seSBhcmVhcyBkb21pbmF0ZWQgYnkgc2hydWJzIGxlc3MgdGhhbiAyMCBjZW50aW1ldGVycyB0YWxsIHdpdGggc2hydWIgY2Fub3B5IHR5cGljYWxseSBncmVhdGVyIHRoYW4gMjAlIG9mIHRvdGFsIHZlZ2V0YXRpb24uIFRoaXMgdHlwZSBpcyBvZnRlbiBjby1hc3NvY2lhdGVkIHdpdGggZ3Jhc3Nlcywgc2VkZ2VzLCBoZXJicywgYW5kIG5vbi12YXNjdWxhciB2ZWdldGF0aW9uLiB8CnwgNTIgICAgICAgICAgfCBTaHJ1Yi9TY3J1YiAtIGFyZWFzIGRvbWluYXRlZCBieSBzaHJ1YnM7IGxlc3MgdGhhbiA1IG1ldGVycyB0YWxsIHdpdGggc2hydWIgY2Fub3B5IHR5cGljYWxseSBncmVhdGVyIHRoYW4gMjAlIG9mIHRvdGFsIHZlZ2V0YXRpb24uIFRoaXMgY2xhc3MgaW5jbHVkZXMgdHJ1ZSBzaHJ1YnMsIHlvdW5nIHRyZWVzIGluIGFuIGVhcmx5IHN1Y2Nlc3Npb25hbCBzdGFnZSBvciB0cmVlcyBzdHVudGVkIGZyb20gZW52aXJvbm1lbnRhbCBjb25kaXRpb25zLiB8CnwgSGVyYmFjZW91cyAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgNzEgICAgICAgICAgfCBHcmFzc2xhbmQvSGVyYmFjZW91cyAtIGFyZWFzIGRvbWluYXRlZCBieSBncmFtYW5vaWQgb3IgaGVyYmFjZW91cyB2ZWdldGF0aW9uLCBnZW5lcmFsbHkgZ3JlYXRlciB0aGFuIDgwJSBvZiB0b3RhbCB2ZWdldGF0aW9uLiBUaGVzZSBhcmVhcyBhcmUgbm90IHN1YmplY3QgdG8gaW50ZW5zaXZlIG1hbmFnZW1lbnQgc3VjaCBhcyB0aWxsaW5nLCBidXQgY2FuIGJlIHV0aWxpemVkIGZvciBncmF6aW5nLiB8CnwgNzIgICAgICAgICAgfCBTZWRnZS9IZXJiYWNlb3VzIC0gQWxhc2thIG9ubHkgYXJlYXMgZG9taW5hdGVkIGJ5IHNlZGdlcyBhbmQgZm9yYnMsIGdlbmVyYWxseSBncmVhdGVyIHRoYW4gODAlIG9mIHRvdGFsIHZlZ2V0YXRpb24uIFRoaXMgdHlwZSBjYW4gb2NjdXIgd2l0aCBzaWduaWZpY2FudCBvdGhlciBncmFzc2VzIG9yIG90aGVyIGdyYXNzLWxpa2UgcGxhbnRzLCBhbmQgaW5jbHVkZXMgc2VkZ2UgdHVuZHJhLCBhbmQgc2VkZ2UgdHVzc29jayB0dW5kcmEuIHwKfCA3MyAgICAgICAgICB8IExpY2hlbnMgLSBBbGFza2Egb25seSBhcmVhcyBkb21pbmF0ZWQgYnkgZnJ1dGljb3NlIG9yIGZvbGlvc2UgbGljaGVucyBnZW5lcmFsbHkgZ3JlYXRlciB0aGFuIDgwJSBvZiB0b3RhbCB2ZWdldGF0aW9uLiB8CnwgNzQgICAgICAgICAgfCBNb3NzIC0gQWxhc2thIG9ubHkgYXJlYXMgZG9taW5hdGVkIGJ5IG1vc3NlcywgZ2VuZXJhbGx5IGdyZWF0ZXIgdGhhbiA4MCUgb2YgdG90YWwgdmVnZXRhdGlvbi4gfAp8IFBsYW50ZWQvQ3VsdGl2YXRlZCB8ICAgICAgICAgICAgICAgICAgICAgIHwKfCA4MSAgICAgICAgICB8IFBhc3R1cmUvSGF5IC0gYXJlYXMgb2YgZ3Jhc3NlcywgbGVndW1lcywgb3IgZ3Jhc3MtbGVndW1lIG1peHR1cmVzIHBsYW50ZWQgZm9yIGxpdmVzdG9jayBncmF6aW5nIG9yIHRoZSBwcm9kdWN0aW9uIG9mIHNlZWQgb3IgaGF5IGNyb3BzLCB0eXBpY2FsbHkgb24gYSBwZXJlbm5pYWwgY3ljbGUuIFBhc3R1cmUvaGF5IHZlZ2V0YXRpb24gYWNjb3VudHMgZm9yIGdyZWF0ZXIgdGhhbiAyMCUgb2YgdG90YWwgdmVnZXRhdGlvbi4gfAp8IDgyICAgICAgICAgIHwgQ3VsdGl2YXRlZCBDcm9wcyAtIGFyZWFzIHVzZWQgZm9yIHRoZSBwcm9kdWN0aW9uIG9mIGFubnVhbCBjcm9wcywgc3VjaCBhcyBjb3JuLCBzb3liZWFucywgdmVnZXRhYmxlcywgdG9iYWNjbywgYW5kIGNvdHRvbiwgYW5kIGFsc28gcGVyZW5uaWFsIHdvb2R5IGNyb3BzIHN1Y2ggYXMgb3JjaGFyZHMgYW5kIHZpbmV5YXJkcy4gQ3JvcCB2ZWdldGF0aW9uIGFjY291bnRzIGZvciBncmVhdGVyIHRoYW4gMjAlIG9mIHRvdGFsIHZlZ2V0YXRpb24uIFRoaXMgY2xhc3MgYWxzbyBpbmNsdWRlcyBhbGwgbGFuZCBiZWluZyBhY3RpdmVseSB0aWxsZWQuIHwKfCBXZXRsYW5kcyAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCA5MCAgICAgICAgICB8IFdvb2R5IFdldGxhbmRzIC0gYXJlYXMgd2hlcmUgZm9yZXN0IG9yIHNocnVibGFuZCB2ZWdldGF0aW9uIGFjY291bnRzIGZvciBncmVhdGVyIHRoYW4gMjAlIG9mIHZlZ2V0YXRpdmUgY292ZXIgYW5kIHRoZSBzb2lsIG9yIHN1YnN0cmF0ZSBpcyBwZXJpb2RpY2FsbHkgc2F0dXJhdGVkIHdpdGggb3IgY292ZXJlZCB3aXRoIHdhdGVyLiB8CnwgOTUgICAgICAgICAgfCBFbWVyZ2VudCBIZXJiYWNlb3VzIFdldGxhbmRzIC0gQXJlYXMgd2hlcmUgcGVyZW5uaWFsIGhlcmJhY2VvdXMgdmVnZXRhdGlvbiBhY2NvdW50cyBmb3IgZ3JlYXRlciB0aGFuIDgwJSBvZiB2ZWdldGF0aXZlIGNvdmVyIGFuZCB0aGUgc29pbCBvciBzdWJzdHJhdGUgaXMgcGVyaW9kaWNhbGx5IHNhdHVyYXRlZCB3aXRoIG9yIGNvdmVyZWQgd2l0aCB3YXRlci4gfAoKCiAKU2V2ZXJhbCByYXN0ZXIgbGF5ZXJzIGhhdmUgYmVlbiBwcm92aWRlZCBmb3IgdGhpcyBhbmFseXNpczogCgotIFdlIHJlYWQgaW4gYEN2aWxsZU1TQWAgLSB0aGlzIGlzIGEgZmlzaG5ldCBjb3ZlcmluZyB0aGUgZXh0ZW50IG9mIG91ciBzdHVkeSBhcmVhIAoKLSBgbGNfY2hhbmdlYCBpcyBhIHJhc3RlciBvZiBsYW5kIGNvdmVyIGNoYW5nZSAtIHdoZXJlIHRoZXJlIHdlcmUgY29udmVyc2lvbnMgYmV0d2VlbiBvbmUgbGFuZCBjb3ZlciBhbmQgYW5vdGhlciBvbiB0aGUgdGltZSBmcmFtZSAyMDAxLTIwMTkuIFdlIHBsb3QgdGhlIHJhc3RlciB1c2luZyBgZ2dwbG90YCBhbmQgdGhlIGByYXN0YCBmdW5jdGlvbiBzcGVjaWZpZWQgYWJvdmUuCgpOb3RlIHRoYXQgYGxjX2NoYW5nZWAgaXMgcHJvamVjdGVkIGFzIGBOQUQgMTk4MyBTdGF0ZVBsYW5lIFZpcmdpbmlhIFNvdXRoIEZJVVNgIGFuZCBpcyBzcGF0aWFsbHkgcmVmZXJlbmNlZCBhcyBgRVBTRzoyMjg0YC4gSXQgaGFzIGFsc28gYmVlbiBjbGlwcGVkIGluIEFyY0dJUyB0byB0aGUgYm91bmRhcnkgb2Ygb3VyIHN0dWR5IGFyZWEuIAoKCmBgYHtyIGxvYWRfZGF0YSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9CkN2aWxsZU1TQSA8LQogICMgSGlnaC1SZXNvbHV0aW9uIFZlcnNpb246CiAgc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2I1MjhkNzI1ZWFlYTVkZDc3OGM0MWZhNjQ0YjVlNmY3MWQ2MjkxYjIvZGF0YS9DdmlsbGVfSGV4Lmdlb2pzb24iKSAlPiUKICAjIE1lZGl1bS1SZXNvbHV0aW9uIFZlcnNpb246CiAgIyBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWWluZ3RvbmctWi9zcHJhd2wtZm9yZWNhc3RpbmcvYTM2NDljNWZmMDY1ODU1MDBjMjE5YjM5OTA5OTEwNjVhZDNjMGE2ZC9kYXRhL0N2aWxsZV9UZXNzZWxhdGVkX0NvYXJzZS5nZW9qc29uIikgJT4lCiAgIyBMb3ctUmVzb2x1dGlvbiBWZXJzaW9uOgogICMgc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2EzNjQ5YzVmZjA2NTg1NTAwYzIxOWIzOTkwOTkxMDY1YWQzYzBhNmQvZGF0YS9DdmlsbGVfVGVzc2VsYXRlZF9Db2Fyc2VyLmdlb2pzb24iKSAlPiUKICBzdF90cmFuc2Zvcm0oJ0VQU0c6MjI4NCcpCgpsY19jaGFuZ2UgPSByYXN0ZXIoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ZaW5ndG9uZy1aL3NwcmF3bC1mb3JlY2FzdGluZy9iNTI4ZDcyNWVhZWE1ZGQ3NzhjNDFmYTY0NGI1ZTZmNzFkNjI5MWIyL2RhdGEvTENfQ2hhbmdlXzIwMDFfMjAxOS50aWYiKQogIApsY19jaGFuZ2UgPC0gcHJvamVjdFJhc3RlcihsY19jaGFuZ2UsIGNycyA9IENSUygiK2luaXQ9ZXBzZzoyMjg0IikpCgpgYGAKCldlIG5vdyBwbG90IExhbmQgQ292ZXIgQ2hhbmdlIHdpdGhpbiBvdXIgTVNBLgoKYGBge3IgcGxvdF9tc2EsIHdhcm5pbmc9IEZBTFNFLCBtZXNzYWdlPSBGQUxTRX0KZ2dwbG90KCkgKwogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY19jaGFuZ2UpICU+JSBuYS5vbWl0ICU+JSBmaWx0ZXIodmFsdWUgPiAwKSwgCiAgICAgICAgICAgICAgYWVzKHgseSxmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpcmVjdGlvbiA9IC0xLCBkaXNjcmV0ZT1UUlVFLCBuYW1lID0iTGFuZCBDb3ZlclxuQ2hhbmdlIikgKwogIGxhYnModGl0bGUgPSAiTGFuZCBDb3ZlciBDaGFuZ2UsIDIwMDAtMjAxOSIpICsKICBtYXBUaGVtZSArCiAgdGhlbWUobGVnZW5kLmRpcmVjdGlvbj0iaG9yaXpvbnRhbCIpCmBgYAoKTmV4dCwgd2UgcmVjbGFzc2lmeSB0aGUgcmFzdGVyIHN1Y2ggdGhhdCBhbGwgdGhlIGRldmVsb3BlZCBncmlkIGNlbGwgdmFsdWVzIHJlY2VpdmUgYSB2YWx1ZSBvZiAxIGFuZCBhbGwgb3RoZXIgdmFsdWVzIHJlY2VpdmUgYSB2YWx1ZSBvZiAwLiAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBUaGlzIGlzIGRvbmUgdXNpbmcgYSByZWNsYXNzaWZ5IG1hdHJpeC4gVGhlIG1hdHJpeCByZWFkcyByb3cgYnkgcm93LiBSb3cgMSBzYXlzIGFueSBncmlkIGNlbGwgcmFuZ2luZyBmcm9tIDAgdG8gMTIgdGFrZXMgYSB2YWx1ZSBvZiAwOyAxMyBvciBncmVhdGVyIHRocm91Z2ggMjQsIGEgdmFsdWUgb2YgMTsgYW5kIGFsbCBvdGhlciB2YWx1ZXMgdGFrZSAwLgpyZWNsYXNzTWF0cml4IDwtICBtYXRyaXgoYygKICAgIDAsMiwwLAogICAgMiwzLDEsCiAgICAzLEluZiwwKSwKICBuY29sPTMsIGJ5cm93PVQpCgpyZWNsYXNzTWF0cml4CmBgYAoKTm93IHdlIGByZWNsYXNzaWZ5YCBhbmQgY29udmVydCBhbGwgMOKAmXMgdG8gYE5BYC4gV2UgYXBwbHkgYSBuYW1lIHRvIHRoZSByYXN0ZXIgd2l0aCBgbmFtZXNgLiBUaGlzIGlzIGRvbmUgdG8gbWFrZSBpdCBmYXN0ZXIgdG8gam9pbiByYXN0ZXIgdG8gdGhlIGZpc2huZXQgYmVsb3cuIAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsY19jaGFuZ2UyIDwtIAogIHJlY2xhc3NpZnkobGNfY2hhbmdlLHJlY2xhc3NNYXRyaXgpCgpsY19jaGFuZ2UyW2xjX2NoYW5nZTIgPCAxXSA8LSBOQQoKbmFtZXMobGNfY2hhbmdlMikgPC0gImxjX2NoYW5nZSIKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Q3ZpbGxlTVNBLCBmaWxsID0gImdyZXkiLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIpKwogIGdlb21fcmFzdGVyKGRhdGE9cmFzdChsY19jaGFuZ2UyKSAlPiUgbmEub21pdCwgCiAgICAgICAgICAgICAgYWVzKHgseSxmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsIG5hbWUgPSJMYW5kIENvdmVyXG5DaGFuZ2UiKSArIAogIGxhYnModGl0bGU9IkRldmVsb3BtZW50IExhbmQgVXNlIENoYW5nZSIpICsKICBtYXBUaGVtZQpgYGAKCgpUaGUgY29kZSBiZWxvdyBjb252ZXJ0cyB0aGUgcmFzdGVyIHRvIGFuIGBzZmAgcG9pbnQgbGF5ZXIgYW5kIHRoZW4gam9pbnMgdGhlIHBvaW50cyB0byB0aGUgZmlzaG5ldCB3aXRoIGBhZ2dyZWdhdGVgLiBGaW5hbGx5LCB0aGUgZmlzaG5ldCB2YXJpYWJsZSBgbGNfY2hhbmdlYCBpcyBjcmVhdGVkIHRoYXQgaXMgYDFgIHdoZXJlIG5ldyBkZXZlbG9wbWVudCBoYXMgb2NjdXJyZWQgYW5kIGAwYCB3aGVyZSBpdCBoYXMgbm90LiBUaGlzIGlzIG91ciBkZXBlbmRlbnQgdmFyaWFibGUgYW5kIGVuY29kZWQgYXMgYSBmYWN0b3IuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmNoYW5nZVBvaW50cyA8LQogIHJhc3RlclRvUG9pbnRzKGxjX2NoYW5nZTIpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSwgY3JzID0gc3RfY3JzKEN2aWxsZU1TQSkpCgpmaXNobmV0IDwtIAogIGFnZ3JlZ2F0ZShjaGFuZ2VQb2ludHMsIEN2aWxsZU1TQSwgc3VtKSAlPiUKICBtdXRhdGUobGNfY2hhbmdlID0gaWZlbHNlKGlzLm5hKGxjX2NoYW5nZSksMCwxKSwKICAgICAgICAgbGNfY2hhbmdlID0gYXMuZmFjdG9yKGxjX2NoYW5nZSkpCiMgUGxvdAojIFRvIHNwZWVkIHVwIHRoZSBtYXBwaW5nIHByb2Nlc3MsIGZpc2huZXQgcG9seWdvbnMgYXJlIGNvbnZlcnRlZCB0byBjZW50cm9pZCBwb2ludHMgdXNpbmcgdGhlIGB4eUNgIGZ1bmN0aW9uIGRlZmluZWQgZWFybGllciAgCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9ZmlzaG5ldCwgYWVzKGZpbGw9bGNfY2hhbmdlKSwgY29sb3I9TkEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLCBuYW1lID0gIiIpICsKICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgRGV2ZWxvcG1lbnQgQ2hhbmdlIiwgc3VidGl0bGUgPSAiQXMgZmlzaG5ldCBjZW50cm9pZHMiKSArCiAgbWFwVGhlbWUKYGBgCgojIyAyLjIuIExhbmQgQ292ZXIgaW4gMjAwMQoKSXQgaXMgcmVhc29uYWJsZSB0byBoeXBvdGhlc2l6ZSB0aGF0IHRoZSBwcm9wZW5zaXR5IG9mIG5ldyBkZXZlbG9wbWVudCBpcyBhIGZ1bmN0aW9uIG9mIGV4aXN0aW5nIGxhbmQgY292ZXIgY2F0ZWdvcmllcy4gSW4gdGhpcyBzZWN0aW9uIHdlIGlkZW50aWZ5IHRoZXNlIG90aGVyIGxhbmQgY292ZXIgY2F0ZWdvcmllcyBmcm9tIDIwMDEgYW5kIGludGVncmF0ZSBlYWNoIHdpdGggdGhlIGZpc2huZXQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxjXzIwMDEgPC0gcmFzdGVyKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWWluZ3RvbmctWi9zcHJhd2wtZm9yZWNhc3RpbmcvYjUyOGQ3MjVlYWVhNWRkNzc4YzQxZmE2NDRiNWU2ZjcxZDYyOTFiMi9kYXRhL0N2aWxsZV9MQ18yMDAxLnRpZiIpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9yYXN0ZXIoZGF0YT1yYXN0KGxjXzIwMDEpICU+JSBuYS5vbWl0ICU+JSBmaWx0ZXIodmFsdWUgPiAwKSwgCiAgICAgICAgICAgICAgYWVzKHgseSxmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsIG5hbWUgPSIiKSArCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyLCAyMDAxIikgKwogIG1hcFRoZW1lICsKICB0aGVtZShsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIikKYGBgCgoKVGhlIHRhYmxlIGJlbG93IHNob3dzIHRoZSBhcHByb2FjaCB0YWtlbiB0byByZWNvZGUgZXhpc3RpbmcgbGFuZCBjb3ZlciBjb2RlcyBpbnRvIHRoZSBjYXRlZ29yaWVzIHVzZWQgaW4gb3VyIGFuYWx5c2lzLiBJbiB0aGUgY29kZSBibG9jayBiZWxvdyBuZXcgcmFzdGVycyBhcmUgZ2VuZXJhdGVkIGFuZCBgbmFtZXNgIGFyZSBhcHBsaWVkLiBOYW1pbmcgZW5zdXJlcyB0aGF0IHdoZW4gdGhlIHJhc3RlciBpcyBpbnRlZ3JhdGVkIHdpdGggdGhlIGZpc2huZXQsIHRoZSBjb2x1bW4gcmVmbGVjdHMgdGhlIGFwcHJvcHJpYXRlIHJhc3Rlci4KCnwgT2xkX0NsYXNzaWZpY2F0aW9uICAgICAgICAgICAgIHwgTmV3X0NsYXNzaWZpY2F0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IE9wZW4gU3BhY2UgYXMgd2VsbCBhcyBMb3csIE1lZGl1bSBhbmQgSGlnaCBJbnRlbnNpdHkgRGV2ZWxvcG1lbnQgfCBEZXZlbG9wZWQgfAp8IERlY2lkdW91cywgRXZlcmdyZWVuLCBhbmQgTWl4ZWQgRm9yZXN0IHwgIEZvcmVzdCB8CnwgUGFzdHVyZS9IYXkgYW5kIEN1bHRpdmF0ZWQgQ3JvcHMgfCBGYXJtIHwKfCBXb29keSBhbmQgRW1lcmdlbnQgSGVyYmFjZW91cyBXZXRsYW5kcyB8IFdvb2RsYW5kcyB8CnwgQmFycmVuIExhbmQsIER3YXJmIFNjcnViLCBhbmQgR3Jhc3NsYW5kL0hlcmJhY2VvdXMgfCBPdGhlciBVbmRldmVsb3BlZCB8CnwgV2F0ZXIgfCBXYXRlciB8CgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRldmVsb3BlZCA8LSBsY18yMDAxID09IDIxIHwgbGNfMjAwMSA9PSAyMiB8IGxjXzIwMDEgPT0gMjMgfCBsY18yMDAxID09IDI0CmZvcmVzdCA8LSBsY18yMDAxID09IDQxIHwgbGNfMjAwMSA9PSA0MiB8IGxjXzIwMDEgPT0gNDMgCmZhcm0gPC0gbGNfMjAwMSA9PSA4MSB8IGxjXzIwMDEgPT0gODIgCndldGxhbmRzIDwtIGxjXzIwMDEgPT0gOTAgfCBsY18yMDAxID09IDk1IApvdGhlclVuZGV2ZWxvcGVkIDwtIGxjXzIwMDEgPT0gNTIgfCBsY18yMDAxID09IDcxIHwgbGNfMjAwMSA9PSAzMSAKd2F0ZXIgPC0gbGNfMjAwMSA9PSAxMQoKbmFtZXMoZGV2ZWxvcGVkKSA8LSAiZGV2ZWxvcGVkIgpuYW1lcyhmb3Jlc3QpIDwtICJmb3Jlc3QiCm5hbWVzKGZhcm0pIDwtICJmYXJtIgpuYW1lcyh3ZXRsYW5kcykgPC0gIndldGxhbmRzIgpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkKSA8LSAib3RoZXJVbmRldmVsb3BlZCIKbmFtZXMod2F0ZXIpIDwtICJ3YXRlciIKYGBgCgpOZXh0LCBlYWNoIHJhc3RlciBpcyBhZ2dyZWdhdGVkIHRvIHRoZSBmaXNobmV0IGJ5IHdheSBvZiBhIGZ1bmN0aW9uIGNhbGxlZCBgYWdncmVnYXRlUmFzdGVyYC4gSGVyZSwgdGhlIHByb2Nlc3MgdXNlZCBhYm92ZSB0byBUbyBkbyB0aGlzLCBhIGZ1bmN0aW9uIGlzIGNyZWF0ZWQgYmVsb3cgdGhhdCBsb29wcyB0aHJvdWdoIGEgbGlzdCBvZiByYXN0ZXJzLCBjb252ZXJ0cyB0aGUgX2l0aF8gcmFzdGVyIHRvIHBvaW50cywgZmlsdGVycyBvbmx5IHBvaW50cyB0aGF0IGhhdmUgdmFsdWUgb2YgYDFgIChpZS4gaXMgdGhlIF9pdGhfIGxhbmQgY292ZXIgdHlwZSksIGFuZCB0aGVuIGFnZ3JlZ2F0ZXMgdG8gdGhlIGZpc2huZXQuCgpIZXJlIGlzIHRoZSBmdW5jdGlvbi4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KYWdncmVnYXRlUmFzdGVyIDwtIGZ1bmN0aW9uKGlucHV0UmFzdGVyTGlzdCwgdGhlRmlzaG5ldCkgewogICNjcmVhdGUgYW4gZW1wdHkgZmlzaG5ldCB3aXRoIHRoZSBzYW1lIGRpbWVuc2lvbnMgYXMgdGhlIGlucHV0IGZpc2huZXQKICB0aGVzZUZpc2huZXRzIDwtIHRoZUZpc2huZXQgJT4lIGRwbHlyOjpzZWxlY3QoKQogICNmb3IgZWFjaCByYXN0ZXIgaW4gdGhlIHJhc3RlciBsaXN0CiAgZm9yIChpIGluIGlucHV0UmFzdGVyTGlzdCkgewogICNjcmVhdGUgYSB2YXJpYWJsZSBuYW1lIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGl0aCByYXN0ZXIKICB2YXJOYW1lIDwtIG5hbWVzKGkpCiAgI2NvbnZlcnQgcmFzdGVyIHRvIHBvaW50cyBhcyBhbiBzZgogICAgdGhlc2VQb2ludHMgPC0KICAgICAgcmFzdGVyVG9Qb2ludHMoaSkgJT4lCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgICAgc3RfYXNfc2YoY29vcmRzID0gYygieCIsICJ5IiksIGNycyA9IHN0X2Nycyh0aGVGaXNobmV0KSkgJT4lCiAgICAgIGZpbHRlciguW1sxXV0gPT0gMSkKICAjYWdncmVnYXRlIHRvIHRoZSBmaXNobmV0CiAgICB0aGlzRmlzaG5ldCA8LQogICAgICBhZ2dyZWdhdGUodGhlc2VQb2ludHMsIHRoZUZpc2huZXQsIGxlbmd0aCkgJT4lCiAgICAgIG11dGF0ZSghIXZhck5hbWUgOj0gaWZlbHNlKGlzLm5hKC5bWzFdXSksMCwxKSkKICAjYWRkIHRvIHRoZSBsYXJnZXIgZmlzaG5ldAogICAgdGhlc2VGaXNobmV0cyA8LSBjYmluZCh0aGVzZUZpc2huZXRzLHRoaXNGaXNobmV0KQogIH0KICAjb3V0cHV0IGFsbCBhZ2dyZWdhdGVzIGFzIG9uZSBsYXJnZSBmaXNobmV0CiAgIHJldHVybih0aGVzZUZpc2huZXRzKQogIH0KYGBgCgpUaGUgYHRoZVJhc3Rlckxpc3RgIG9mIGxhbmQgY292ZXIgdHlwZXMgaW4gMjAwMSBpcyBjcmVhdGVkIGFuZCB0aGVuIGZlZCBpbnRvIGBhZ2dyZWdhdGVSYXN0ZXJgLiBUaGUgcmVzdWx0IGlzIGNvbnZlcnRlZCB0byBsb25nIGZvcm0gZ3JpZCBjZWxsIGNlbnRyb2lkcyBhbmQgcGxvdCBhcyBzbWFsbCBtdWx0aXBsZSBtYXBzLgoKTm90ZSB0aGF0IHNpbmNlIG91ciBsYW5kIGNvdmVyIGRhdGEgaXMgYXQgYSBkaWZmZXJlbnQgcmVzb2x1dGlvbiB0aGFuIG91ciBmaXNobmV0LCB3ZSBoYXZlIHJlYXNzaWduZWQgY2VsbHMgd2l0aCBtb3JlIHRoYW4gb25lIGxhbmQgY292ZXIgdmFsdWUgdG8ganVzdCBvbmUgYmFzZWQgb24gYSBoaWVyYXJjaHkuIFRoaXMgbWV0aG9kIGNhbiBpbnRyb2R1Y2Ugc29tZSBkZWdyZWUgb2YgZXJyb3IsIHdoaWNoIGlzIHdvcnNlIHdpdGggbG93ZXItcmVzb2x1dGlvbiBmaXNobmV0cy4gRm9yIG91ciBwdXJwb3NlcywgdXNpbmcgYSBoaWdoLXJlc29sdXRpb24gZmlzaG5ldCwgaXQgcGVyZm9ybXMganVzdCBmaW5lLiBMYXRlciBpdGVyYXRpb25zIG9mIHRoaXMgd29ya2Zsb3cgd2lsbCBpbmNsdWRlIGEgbWV0aG9kIGZvciBhc3NpZ25pbmcgdGhlIG1vc3QgY29tbW9uIGxhbmQgY292ZXIgdHlwZSB3aXRoaW4gZWFjaCBmaXNobmV0IGNlbGwgdG8gdGhhdCBjZWxsLCB3aGljaCBzaG91bGQgcmVkdWNlIHRoZSBkZWdyZWUgb2YgZXJyb3IgdGhhdCBjb21lcyB3aXRoIGxvd2VyLXJlc29sdXRpb24gZmlzaG5ldHMuCgpOb3RlIGFsc28gdGhlIGluY2x1c2lvbiBvZiBgc3RfY2FzdGAgaGVyZSB3aGljaCBjb252ZXJ0IGFsbCBnZW9tZXRyaWVzIHRvIGBQT0xZR09OYC4gSWYgd2UgY3JlYXRlIGEgZnJlcXVlbmN5IHRhYmxlIG9mIGdlb21ldHJ5IHR5cGVzIGluIGBhZ2dyZWdhdGVkUmFzdGVyc2AsIHdlIHdpbGwgbm90aWNlIHNvbWUgYW5kIGhhbmRmdWwgb2YgYE1VTFRJUE9MWUdPTlNgLiBUcnkgYHRhYmxlKHN0X2dlb21ldHJ5X3R5cGUoYWdncmVnYXRlZFJhc3RlcnMpYCkuIFRoZXNlIHJvZ3VlIG11bHRpcG9seWdvbnMgYnJlYWsgdGhlIGB4eUNgIGZ1bmN0aW9uIHdoaWNoIGlzIGRlc2lnbmVkIHRvIGZpbmQgZ3JpZCBjZWxsIGNlbnRyb2lkcy4gQWZ0ZXIgYWxsLCB0aGVyZSBpcyBubyBvbmUgY2VudHJvaWQgb2Ygc2V2ZXJhbCBjb21iaW5lZCBwb2x5Z29ucy4gVGh1cyBgc3RfY2FzdGAgZW5zdXJlcyBhbGwgZ2VvbWV0cmllcyBhcmUganVzdCBgUE9MWUdPTmAuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnRoZVJhc3Rlckxpc3QgPC0gYyhkZXZlbG9wZWQsZm9yZXN0LGZhcm0sd2V0bGFuZHMsb3RoZXJVbmRldmVsb3BlZCx3YXRlcikKCmFnZ3JlZ2F0ZWRSYXN0ZXJzIDwtCiAgYWdncmVnYXRlUmFzdGVyKHRoZVJhc3Rlckxpc3QsIEN2aWxsZU1TQSkgJT4lCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wZWQsZm9yZXN0LGZhcm0sd2V0bGFuZHMsb3RoZXJVbmRldmVsb3BlZCx3YXRlcikgJT4lCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsYXMuZmFjdG9yKQoKIyByZWFzc2lnbiBjZWxscyB3aXRoIG1vcmUgdGhhbiBvbmUgbGMgdmFsdWUgdG8ganVzdCBvbmUgYmFzZWQgb24gaGllcmFyY2h5IHNob3duIGhlcmUKYWdncmVnYXRlZFJhc3RlcnMgPC0gYWdncmVnYXRlZFJhc3RlcnMgJT4lCiAgICBtdXRhdGUoZmFybSA9IGlmZWxzZShkZXZlbG9wZWQgPT0gMCAmIGZhcm0gPT0gMSwgMSwgMCkpICU+JQogICAgbXV0YXRlKGZvcmVzdCA9IGlmZWxzZShkZXZlbG9wZWQgPT0gMCAmIGZhcm0gPT0gMCAmIGZvcmVzdCA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUod2V0bGFuZHMgPSBpZmVsc2UoZGV2ZWxvcGVkID09IDAgJiBmYXJtID09IDAgJiBmb3Jlc3QgPT0gMCAmIHdldGxhbmRzID09IDEsIDEsIDApKSAlPiUKICAgIG11dGF0ZSh3YXRlciA9IGlmZWxzZShkZXZlbG9wZWQgPT0gMCAmIGZhcm0gPT0gMCAmIGZvcmVzdCA9PSAwICYgd2V0bGFuZHMgPT0gMCAmIHdhdGVyID09IDEsIDEsIDApKSAlPiUKICAgIG11dGF0ZShvdGhlclVuZGV2ZWxvcGVkID0gaWZlbHNlKGRldmVsb3BlZCA9PSAwICYgZmFybSA9PSAwICYgZm9yZXN0ID09IDAgJiB3ZXRsYW5kcyA9PSAwICYgd2F0ZXIgPT0gMCAmIG90aGVyVW5kZXZlbG9wZWQgPT0gMSwgMSwgMCkpCgphZ2dyZWdhdGVkUmFzdGVycyAlPiUKICBnYXRoZXIodmFyLHZhbHVlLGRldmVsb3BlZDp3YXRlcikgJT4lCiAgc3RfY2FzdCgiUE9MWUdPTiIpICU+JSAgICAjanVzdCB0byBtYWtlIHN1cmUgbm8gd2VpcmQgZ2VvbWV0cmllcyBzbGlwcGVkIGluCiAgbXV0YXRlKFggPSB4eUMoLikkeCwKICAgICAgICAgWSA9IHh5QyguKSR5KSAlPiUKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3NmKGRhdGE9Q3ZpbGxlTVNBKSArCiAgICBnZW9tX3BvaW50KGFlcyhYLFksIGNvbG91cj1hcy5mYWN0b3IodmFsdWUpKSwgc2l6ZSA9MC4xKSArCiAgICBmYWNldF93cmFwKH52YXIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMDEiLAogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICAgbWFwVGhlbWUKYGBgCgojIyAyLjMuIENlbnN1cyBEYXRhCgpQb3B1bGF0aW9uIGFuZCBwb3B1bGF0aW9uIGNoYW5nZSBhcmUgY3J1Y2lhbCBkZW1hbmQtc2lkZSBmYWN0b3JzIGZvciBwcmVkaWN0aW5nIGBEZXZlbG9wbWVudF9EZW1hbmRgLiBXZSBvYnRhaW4gMjAwMCBhbmQgMjAyMCBjZW5zdXMgZGF0YSB0aHJvdWdoIHRoZSBgdGlkeWNlbnN1c2AgcGFja2FnZSB0byByZXByZXNlbnQgdGhlIGRlbW9ncmFwaGljIGZlYXR1cmVzIGZvciAyMDAxIGFuZCAyMDE5LiBUaGVzZSBkYXRhIGFyZSBkb3dubG9hZGVkIGF0IGEgYmxvY2sgZ3JvdXAgZ2VvZ3JhcGh5IGFuZCB0aHVzIGFuIGFwcHJvYWNoIGlzIG5lZWRlZCB0byByZWNvbmNpbGUgdHJhY3RzIGFuZCBmaXNobmV0IGdlb21ldHJpZXMuIFRoaXMgaXMgYWNjb21wbGlzaGVkIHVzaW5nIGEgdGVjaG5pcXVlIGNhbGxlZCBhcmVhbCB3ZWlnaHRlZCBpbnRlcnBvbGF0aW9uLgoKQmVsb3cgd2UgZ2V0IHRoZSBkZW1vZ3JhcGhpYyBkYXRhIGluY2x1ZGluZyBgdG90YWxfcG9wdWxhdGlvbmAsIGB0b3RhbF9ob3VzaW5nX3VuaXRzYCwgYHRvdGFsX3ZhY2FudF9ob3VzZWhvbGRzYCwgYHRvdGFsX3doaXRlYCxgdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnNgIGZvciBib3RoIDIwMDAgYW5kIDIwMjAuCgpgYGB7ciBsb2FkX2tleV9oaWRlLCB3YXJuaW5nPSBGQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgQVBJIGtleQpjZW5zdXNfYXBpX2tleSgiMjBhZDM4YzZkOWUyNDFiZTQ5YWUzYTFhNmY3NDEyNTQ0OGRiNjc0ZSIsIGluc3RhbGwgPSBUUlVFLCBvdmVyd3JpdGUgPSBUUlVFKQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KCiNmdW5jdGlvbiB0byBnZXQgYW5kIGNsZWFuIDIwMDAgY2Vuc3VzIGRhdGEgKGRlY2VubmlhbCkKY2xlYW5fMjAwMF9jZW5zdXNfZGF0YSA8LSBmdW5jdGlvbigpIHsKICAKICAjdmFyaWJsZXMgdG8gdXNlIGZvciAyMDAwIGRlY2VuaXRhbCB2YXJpYWJsZXMKICB2YXJpYWJsZXMyMDAwQSA8LSBjKCJQMDAxMDAxIiwgIkgwMDEwMDEiLCAiSDAwMzAwMyIsICJQMDAzMDAzIikKICBuYW1lcyh2YXJpYWJsZXMyMDAwQSkgPC0gYygidG90YWxfcG9wdWxhdGlvbiIsICJ0b3RhbF9ob3VzaW5nX3VuaXRzIiwgInRvdGFsX3ZhY2FudF9ob3VzZWhvbGRzIiwgInRvdGFsX3doaXRlIikKICAKICB2YXJpYWJsZXMyMDAwQiA8LSBjKCJQMDUzMDAxIiwiUDAzNjAyMSIsIlAwMzYwNDQiKQogIG5hbWVzKHZhcmlhYmxlczIwMDBCKSA8LSBjKCJtZWRpYW5faG91c2Vob2xkX2luY29tZSIsIk1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnMiLCAiRmVtYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzIikKICAKICAjZnVuY3Rpb24gdG8gZ2V0IDIwMDAgZGVjZW5uaWFsIHZhcmlhYmxlcwogIGdldF9kZWNlbmlhbF92YXJpYWJsZXMgPC0gZnVuY3Rpb24oeWVhciwgc3VtZmlsZSwgdmFyaWFibGVzLCBjb3VudHkpIHsKICAgIGRhdGEgPC0gZ2V0X2RlY2VubmlhbCgKICAgICAgZ2VvZ3JhcGh5ID0gImJsb2NrIGdyb3VwIiwKICAgICAgeWVhciA9IHllYXIsCiAgICAgIHN1bWZpbGUgPSBzdW1maWxlLAogICAgICB2YXJpYWJsZXMgPSB2YXJpYWJsZXMsCiAgICAgIHN0YXRlID0gIlZBIiwKICAgICAgY291bnR5ID0gY291bnR5LAogICAgICBvdXRwdXQgPSAid2lkZSIsCiAgICAgIGdlb21ldHJ5ID0gVFJVRQogICAgKQogICAgCiAgICByZXR1cm4oZGF0YSkKICB9CiAgCiAgIyBnZXQgdmFyaWFibGVzIGZyb20gZGlmZmVyZW50IGRlY2VubmlhbCBmaWxlcyBmb3IgQ1YgYW5kIEFNCiAgZGF0YV9jdjEgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjEnLCB2YXJpYWJsZXMyMDAwQSwgIkNoYXJsb3R0ZXN2aWxsZSIpCiAgZGF0YV9jdjIgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjMnLCB2YXJpYWJsZXMyMDAwQiwgIkNoYXJsb3R0ZXN2aWxsZSIpCiAgZGF0YV9hbTEgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjEnLCB2YXJpYWJsZXMyMDAwQSwgIkFsYmVtYXJsZSIpCiAgZGF0YV9hbTIgPC0gZ2V0X2RlY2VuaWFsX3ZhcmlhYmxlcygyMDAwLCdzZjMnLCB2YXJpYWJsZXMyMDAwQiwgIkFsYmVtYXJsZSIpCiAgCiAgIyBqb2luIHRoZSBkYXRhCiAgY3ZhbV9jZW5zdXMxIDwtIHJiaW5kKGRhdGFfY3YxLCBkYXRhX2FtMSkKICBjdmFtX2NlbnN1czIgPC0gcmJpbmQoZGF0YV9jdjIsIGRhdGFfYW0yKQogIAogIGN2YW1fY2Vuc3VzIDwtIHN0X2pvaW4oY3ZhbV9jZW5zdXMxLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGN2YW1fY2Vuc3VzMlssIGMoIm1lZGlhbl9ob3VzZWhvbGRfaW5jb21lIiwgIk1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnMiLCAiRmVtYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzIildLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGpvaW4gPSBzdF9lcXVhbHMpICAlPiUKICAgIG11dGF0ZShgdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnNgID0gYE1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgICsgYEZlbWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVyc2ApICU+JQogICAgZHBseXI6OnNlbGVjdCgtYE1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgLCAtYEZlbWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVyc2ApCiAgCiAgcmV0dXJuKGN2YW1fY2Vuc3VzKQp9CgoKCiNmdW5jdGlvbiB0byBnZXQgYW5kIGNsZWFuIDIwMjAgY2Vuc3VzIGRhdGEgKGFjczUpCmNsZWFuXzIwMjBfY2Vuc3VzX2RhdGEgPC0gZnVuY3Rpb24oKSB7CiAgCiAgI3ZhcmlibGVzIHRvIHVzZSBmb3IgMjAyMCBkZWNlbml0YWwgdmFyaWFibGVzCiAgdmFyaWFibGVzMjAyMEEgPC0gYygiQjAxMDAzXzAwMUUiLCAiQjI1MDAxXzAwMUUiLCAiQjI1MDAyXzAwM0UiLCAiQjAyMDAxXzAwMkUiKQogIG5hbWVzKHZhcmlhYmxlczIwMjBBKSA8LSBjKCJ0b3RhbF9wb3B1bGF0aW9uIiwgInRvdGFsX2hvdXNpbmdfdW5pdHMiLCAidG90YWxfdmFjYW50X2hvdXNlaG9sZHMiLCAidG90YWxfd2hpdGUiKQogIAogIHZhcmlhYmxlczIwMjBCIDwtIGMoIkIxOTA0OV8wMDFFIiwiQjE0MDAyXzAyMkUiLCJCMTQwMDJfMDQ2RSIpCiAgbmFtZXModmFyaWFibGVzMjAyMEIpIDwtIGMoIm1lZGlhbl9ob3VzZWhvbGRfaW5jb21lIiwiTWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVycyIsICJGZW1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnMiKQogIAogICNmdW5jdGlvbiB0byBnZXQgMjAyMCBkZWNlbml0YWwgdmFyaWFibGVzCiAgZ2V0X2Fjc192YXJpYWJsZXMgPC0gZnVuY3Rpb24oeWVhciwgdmFyaWFibGVzLCBjb3VudHkpIHsKICAgIGRhdGEgPC0gZ2V0X2FjcygKICAgICAgZ2VvZ3JhcGh5ID0gImJsb2NrIGdyb3VwIiwKICAgICAgc3VydmV5ID0gImFjczUiLAogICAgICB5ZWFyID0geWVhciwKICAgICAgdmFyaWFibGVzID0gdmFyaWFibGVzLAogICAgICBzdGF0ZSA9ICJWQSIsCiAgICAgIGNvdW50eSA9IGNvdW50eSwKICAgICAgb3V0cHV0ID0gIndpZGUiLAogICAgICBnZW9tZXRyeSA9IFRSVUUKICAgICkKICAgIAogICAgcmV0dXJuKGRhdGEpCiAgfQogIAogICMgZ2V0IHZhcmlhYmxlcyBmcm9tIGRpZmZlcmVudCBhY3MgZmlsZXMgZm9yIENWIGFuZCBBTQogIGRhdGFfY3YxIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBBLCAiQ2hhcmxvdHRlc3ZpbGxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIGRhdGFfY3YyIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBCLCAiQ2hhcmxvdHRlc3ZpbGxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIGRhdGFfYW0xIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBBLCAiQWxiZW1hcmxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIGRhdGFfYW0yIDwtIGdldF9hY3NfdmFyaWFibGVzKDIwMjAsIHZhcmlhYmxlczIwMjBCLCAiQWxiZW1hcmxlIikgJT4lIGRwbHlyOjpzZWxlY3QoIWVuZHNfd2l0aCgiTSIpKQogIAogICMgam9pbiB0aGUgZGF0YQogIGN2YW1fY2Vuc3VzMSA8LSByYmluZChkYXRhX2N2MSwgZGF0YV9hbTEpCiAgY3ZhbV9jZW5zdXMyIDwtIHJiaW5kKGRhdGFfY3YyLCBkYXRhX2FtMikKICAKICBjdmFtX2NlbnN1cyA8LSBzdF9qb2luKGN2YW1fY2Vuc3VzMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjdmFtX2NlbnN1czJbLCBjKCJtZWRpYW5faG91c2Vob2xkX2luY29tZSIsICJNYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzIiwgIkZlbWFsZSBUb3RhbCBncmFkdWF0ZSBkZWdyZWUgaG9sZGVycyIpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBqb2luID0gc3RfZXF1YWxzKSAgJT4lCiAgICBtdXRhdGUoYHRvdGFsX2dyYWR1YXRlX2RlZ3JlZV9ob2xkZXJzYCA9IGBNYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzYCArIGBGZW1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWBNYWxlIFRvdGFsIGdyYWR1YXRlIGRlZ3JlZSBob2xkZXJzYCwgLWBGZW1hbGUgVG90YWwgZ3JhZHVhdGUgZGVncmVlIGhvbGRlcnNgKQogIAogIHJldHVybihjdmFtX2NlbnN1cykKfQoKCiMgcnVuIHRoZSBmdW5jdGlvbnMgCmN2YW1fMjAwMF9jZW5zdXMgPC0gY2xlYW5fMjAwMF9jZW5zdXNfZGF0YSgpICMgMjAwMCBjZW5zdXMgZGF0YQpjdmFtXzIwMjBfY2Vuc3VzIDwtIGNsZWFuXzIwMjBfY2Vuc3VzX2RhdGEoKSAjIDIwMjAgY2Vuc3VzIGRhdGEKYGBgCgpBZGRpdGlvbmFsIENlbnN1cyBEYXRhIEZvcm1hdHRpbmcgZm9yIDIwMjAuCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KY3ZhbV8yMDIwX2NlbnN1cyA8LSBjdmFtXzIwMjBfY2Vuc3VzICU+JSAKICBtdXRhdGUodG90YWxfcG9wdWxhdGlvbjIwMjAgPSB0b3RhbF9wb3B1bGF0aW9uKSAlPiUKICBtdXRhdGUodG90YWxfaG91c2luZ191bml0czIwMjAgPSB0b3RhbF9ob3VzaW5nX3VuaXRzKSAlPiUKICBtdXRhdGUodG90YWxfdmFjYW50X2hvdXNlaG9sZHMyMDIwID0gdG90YWxfdmFjYW50X2hvdXNlaG9sZHMpICU+JQogIG11dGF0ZSh0b3RhbF93aGl0ZTIwMjAgPSB0b3RhbF93aGl0ZSkgJT4lCiAgbXV0YXRlKG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lMjAyMCA9IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lKSAlPiUKICBtdXRhdGUodG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnMyMDIwID0gdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnMpICU+JQogIGRwbHlyOjpzZWxlY3QoLWB0b3RhbF9wb3B1bGF0aW9uYCwgLWB0b3RhbF9ob3VzaW5nX3VuaXRzYCwgLWB0b3RhbF92YWNhbnRfaG91c2Vob2xkc2AsIC1gdG90YWxfd2hpdGVgLCAtYG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lYCwgLWB0b3RhbF9ncmFkdWF0ZV9kZWdyZWVfaG9sZGVyc2ApCmBgYAoKRmVhdHVyZSBvZiAyMDAwIHRvdGFsIHBvcHVsYXRpb24gZGF0YSBpcyBwbG90dGVkLgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmdncGxvdCgpKwogIGdlb21fc2YoZGF0YSA9IGN2YW1fMjAwMF9jZW5zdXMsIAogICAgICAgICAgYWVzKGZpbGwgPSB0b3RhbF9wb3B1bGF0aW9uKSkgKyBtYXBUaGVtZQpgYGAKClRvIGpvaW4gdGhlIGRlbW9ncmFwaGljIGRhdGEgdG8gZmlzaG5ldCwgYSBzcGF0aWFsIGpvaW4gd291bGQgYmUgaW5hcHByb3ByaWF0ZSBhcyBpdCB3b3VsZCBhc3NpZ24gdGhlIHNhbWUgZGVtb2dyYXBoaWMgZmVhdHVyZSB2YWx1ZSBmcm9tIG9uZSB0cmFjdCB0byB0aGUgbWFueSBpbnRlcnNlY3RpbmcgZ3JpZCBjZWxscy4gSW5zdGVhZCwgdGhlIGFyZWEgd2VpZ2h0ZWQgaW50ZXJwb2xhdGlvbiBmdW5jdGlvbiwgYHN0X2ludGVycG9sYXRlX2F3YCwgYXNzaWducyBhIHByb3BvcnRpb24gb2YgYSB0cmFjdOKAmXMgZGVtb2dyYXBoaWMgZmVhdHVyZSB0byBhIGdyaWQgY2VsbCB3ZWlnaHRlZCBieSB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgdHJhY3QgdGhhdCBpbnRlcnNlY3RzIHRoZSBncmlkIGNlbGwuIFRoaXMgd29ya3MgYmVzdCB3aGVuIHdlIGFzc3VtZSB0aGF0IHRoZSBibG9jayBncm91cCBwb3B1bGF0aW9uIGlzIHVuaWZvcm1seSBkaXN0cmlidXRlZCBhY3Jvc3MgdGhlIGJsb2NrIGdyb3VwLiBUaGlzIGlzIHR5cGljYWxseSBub3QgYSBncmVhdCBhc3N1bXB0aW9uLiBIb3dldmVyLCBpdCBpcyBhIHJlYXNvbmFibGUgaGVyZSBwYXJ0aWN1bGFybHkgZ2l2ZW4gZGVtb2dyYXBoaWMgZmVhdHVyZXMgaW4gYSByZWdyZXNzaW9uIGFuZCBub3QgYW4gb3V0Y29tZSB0aGF0IG5lZWRzIHRvIGJlIG1lYXN1cmVkIHdpdGggc2lnbmlmaWNhbnQgcHJlY2lzaW9uLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpDdmlsbGVNU0EgPC0KICBDdmlsbGVNU0EgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJmaXNobmV0SUQiKSAlPiUgCiAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLm51bWVyaWMoZmlzaG5ldElEKSkgJT4lCiAgZHBseXI6OnNlbGVjdChmaXNobmV0SUQpCgojIFRyYW5zZm9ybSBQcm9qZWN0aW9uIG9mIENlbnN1cyBEYXRhCmN2YW1fMjAwMF9jZW5zdXMgPC0gY3ZhbV8yMDAwX2NlbnN1cyAlPiUgCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhDdmlsbGVNU0EpKQoKY3ZhbV8yMDIwX2NlbnN1cyA8LSBjdmFtXzIwMjBfY2Vuc3VzICU+JSAKICBzdF90cmFuc2Zvcm0oc3RfY3JzKEN2aWxsZU1TQSkpCmBgYAoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBKb2luIGludGVycG9sYXRlZCBjZW5zdXMgZGF0YSB3aXRoIGZpc2huZXQKCiMgMjAwMCBpbnRlcnBvbGF0aW9uIGZ1bmN0aW9uCmludGVycG9sYXRlX2NvbHVtbjIwMDAgPC0gZnVuY3Rpb24oY29sdW1uX25hbWUpIHsKICAKICBpbnRlcnBvbGF0ZWRfZGF0YSA8LQogICAgc3RfaW50ZXJwb2xhdGVfYXcoY3ZhbV8yMDAwX2NlbnN1c1tjb2x1bW5fbmFtZV0sIEN2aWxsZU1TQSwgZXh0ZW5zaXZlPVRSVUUpICU+JQogICAgYXMuZGF0YS5mcmFtZSguKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiZmlzaG5ldElEIikgJT4lCiAgICBsZWZ0X2pvaW4oQ3ZpbGxlTVNBICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKGZpc2huZXRJRCA9IGFzLmNoYXJhY3RlcihmaXNobmV0SUQpKSwKICAgICAgICAgICAgICAuLCBieT1jKCJmaXNobmV0SUQiPSdmaXNobmV0SUQnKSkgJT4lIAogICAgbXV0YXRlKCEhY29sdW1uX25hbWUgOj0gcmVwbGFjZV9uYSghIXN5bShjb2x1bW5fbmFtZSksIDApKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoY29sdW1uX25hbWUpCiAgCiAgcmV0dXJuKGludGVycG9sYXRlZF9kYXRhKQp9CgpmaXNobmV0UG9wdWxhdGlvbjAwIDwtIGludGVycG9sYXRlX2NvbHVtbjIwMDAoJ3RvdGFsX3BvcHVsYXRpb24nKQpmaXNobmV0SEh1bml0MDAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAwMCgndG90YWxfaG91c2luZ191bml0cycpCmZpc2huZXRWYWNISDAwIDwtIGludGVycG9sYXRlX2NvbHVtbjIwMDAoJ3RvdGFsX3ZhY2FudF9ob3VzZWhvbGRzJykKZmlzaG5ldFdoaXRlUDAwIDwtIGludGVycG9sYXRlX2NvbHVtbjIwMDAoJ3RvdGFsX3doaXRlJykKZmlzaG5ldEdyYWR1YXRlMDAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAwMCgndG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnMnKQoKIyAyMDIwIGludGVycG9sYXRpb24gZnVuY3Rpb24KaW50ZXJwb2xhdGVfY29sdW1uMjAyMCA8LSBmdW5jdGlvbihjb2x1bW5fbmFtZSkgewogIAogIGludGVycG9sYXRlZF9kYXRhIDwtCiAgICBzdF9pbnRlcnBvbGF0ZV9hdyhjdmFtXzIwMjBfY2Vuc3VzW2NvbHVtbl9uYW1lXSwgQ3ZpbGxlTVNBLCBleHRlbnNpdmU9VFJVRSkgJT4lCiAgICBhcy5kYXRhLmZyYW1lKC4pICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJmaXNobmV0SUQiKSAlPiUKICAgIGxlZnRfam9pbihDdmlsbGVNU0EgJT4lCiAgICAgICAgICAgICAgICBtdXRhdGUoZmlzaG5ldElEID0gYXMuY2hhcmFjdGVyKGZpc2huZXRJRCkpLAogICAgICAgICAgICAgIC4sIGJ5PWMoImZpc2huZXRJRCI9J2Zpc2huZXRJRCcpKSAlPiUgCiAgICBtdXRhdGUoISFjb2x1bW5fbmFtZSA6PSByZXBsYWNlX25hKCEhc3ltKGNvbHVtbl9uYW1lKSwgMCkpICU+JQogICAgZHBseXI6OnNlbGVjdChjb2x1bW5fbmFtZSkKICAKICByZXR1cm4oaW50ZXJwb2xhdGVkX2RhdGEpCn0KCmZpc2huZXRQb3B1bGF0aW9uMjAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAyMCgndG90YWxfcG9wdWxhdGlvbjIwMjAnKQpmaXNobmV0SEh1bml0MjAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAyMCgndG90YWxfaG91c2luZ191bml0czIwMjAnKQpmaXNobmV0V2hpdGVQMjAgPC0gaW50ZXJwb2xhdGVfY29sdW1uMjAyMCgndG90YWxfd2hpdGUyMDIwJykKCmZpc2huZXRfcG9wX2NoYW5nZV8wMF8yMCA8LSAKICBjYmluZChmaXNobmV0UG9wdWxhdGlvbjAwLGZpc2huZXRQb3B1bGF0aW9uMjApICU+JQogIGRwbHlyOjpzZWxlY3QodG90YWxfcG9wdWxhdGlvbix0b3RhbF9wb3B1bGF0aW9uMjAyMCkgJT4lCiAgbXV0YXRlKHBvcF9DaGFuZ2VfMDBfMjAgPSB0b3RhbF9wb3B1bGF0aW9uMjAyMCAtIHRvdGFsX3BvcHVsYXRpb24pCgojcGxvdCBkYXRhIGZyb20gMjAwMApnZ3Bsb3QoKSsKICBnZW9tX3NmKGRhdGEgPSBmaXNobmV0UG9wdWxhdGlvbjAwLCAKICAgICAgICAgIGFlcyhmaWxsID0gdG90YWxfcG9wdWxhdGlvbiksIGNvbG9yID0gInRyYW5zcGFyZW50IikrIG1hcFRoZW1lCmBgYAoKCiMjIDIuNC4gSGlnaHdheSBEaXN0YW5jZQoKQWNjZXNzaWJpbGl0eSBpcyBhIGtleSBkZXRlcm1pbmFudCBvZiBkZXZlbG9wbWVudCBwb3RlbnRpYWwgcGFydGljdWxhcmx5IGluIGEgc3ByYXdsaW5nIGNpdHkgbGlrZSBDaGFybG90dGVzdmlsbGUuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyB3b3JrZmxvdywgYWNjZXNzaWJpbGl0eSBmZWF0dXJlcyBhcmUgZW5naW5lZXJlZCBieSBtZWFzdXJpbmcgZGlzdGFuY2UgZnJvbSBlYWNoIGdyaWQgY2VsbCB0byBpdHMgbmVhcmVzdCBoaWdod2F5LgoKRmlyc3QgaGlnaHdheSB2ZWN0b3JzIGFyZSBpbXBvcnRlZCBpbiBgZ2VvanNvbmAgZm9ybWF0OyBwcm9qZWN0ZWQgYW5kIHN1YnNldCB0byB0aGUgc3Vic2V0IHVzaW5nIGBzdF9pbnRlcnNlY3Rpb25gLiBCZWxvdywgbmV3IGRldmVsb3BtZW50IGlzIG1hcHBlZCB3aXRoIHRoZSBoaWdod2F5IG92ZXJsYXkuCgpgYGB7ciByZWFkIGFuZCBwbG90IGhpZ2h3YXksIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT0gRkFMU0UscmVzdWx0cyA9ICJoaWRlIn0KQ3ZpbGxlSGlnaHdheXMgPC0KICBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWWluZ3RvbmctWi9zcHJhd2wtZm9yZWNhc3RpbmcvYjUyOGQ3MjVlYWVhNWRkNzc4YzQxZmE2NDRiNWU2ZjcxZDYyOTFiMi9kYXRhL0N2aWxsZV9IaWdod2F5cy5nZW9qc29uIikgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhDdmlsbGVNU0EpKSAlPiUKICBzdF9pbnRlcnNlY3Rpb24oQ3ZpbGxlTVNBKQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1maXNobmV0LCAKICAgICAgICAgICAgIGFlcyh4PXh5QyhmaXNobmV0KVssMV0sIHk9eHlDKGZpc2huZXQpWywyXSxjb2xvdXI9bGNfY2hhbmdlKSxzaXplPTAuMSkgKwogIGdlb21fc2YoZGF0YT1DdmlsbGVIaWdod2F5cywgc2l6ZSA9IDEpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUyLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJOZXcgRGV2ZWxvcG1lbnQgYW5kIEhpZ2h3YXlzIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKVGhlIGRpc3RhbmNlIGZyb20gZWFjaCBncmlkIGNlbGwgdG8gaXRzIG5lYXJlc3QgaGlnaHdheSBzZWdtZW50IGlzIG1lYXN1cmVkLgoKRmlyc3QsIHRoZSBoaWdod2F5IGxheWVyIGlzIGNvbnZlcnRlZCB0byByYXN0ZXIuIFRoaXMgaXMgZG9uZSBieSBjcmVhdGluZyBhbiBgZW1wdHlSYXN0ZXJgIG9mIGBOQWAgZ3JpZCBjZWxscyBhdCB0aGUgc2FtZSBzcGF0aWFsIGV4dGVudCBhcyBgbGNfY2hhbmdlYC4gVGhlbiwgYGhpZ2h3YXlfcmFzdGVyYCBpcyBjcmVhdGVkIGJ5IGNvbnZlcnRpbmcgYEN2aWxsZUhpZ2h3YXlzYCB0byBgc3BgIGZvcm0gYW5kIHRoZW4gdG8gYXBwbHlpbmcgYHJhc3Rlcml6ZWAuIFRoZSByYXN0ZXIgaXMgdGhlbiBjb252ZXJ0ZWQgdG8gcG9pbnRzIHdpdGggYHJhc3RlclRvUG9pbnRzYCBhbmQgYHN0X2FzX3NmYCwgdGhlbiBgYWdncmVnYXRlYCBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBtZWFuIGRpc3RhbmNlIGJ5IGdyaWQgY2VsbC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZW1wdHlSYXN0ZXIgPC0gbGNfY2hhbmdlCmVtcHR5UmFzdGVyW10gPC0gTkEKCmhpZ2h3YXlfcmFzdGVyIDwtIAogIGFzKEN2aWxsZUhpZ2h3YXlzLCdTcGF0aWFsJykgJT4lCiAgcmFzdGVyaXplKC4sZW1wdHlSYXN0ZXIpCgpoaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSA8LSBkaXN0YW5jZShoaWdod2F5X3Jhc3RlcikKbmFtZXMoaGlnaHdheV9yYXN0ZXJfZGlzdGFuY2UpIDwtICJkaXN0YW5jZV9oaWdod2F5cyIKCmhpZ2h3YXlQb2ludHMgPC0KICByYXN0ZXJUb1BvaW50cyhoaWdod2F5X3Jhc3Rlcl9kaXN0YW5jZSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpLCBjcnMgPSBzdF9jcnMoQ3ZpbGxlTVNBKSkKCmhpZ2h3YXlQb2ludHNfZmlzaG5ldCA8LSAKICBhZ2dyZWdhdGUoaGlnaHdheVBvaW50cywgQ3ZpbGxlTVNBLCBtZWFuKSAlPiUKICBtdXRhdGUoZGlzdGFuY2VfaGlnaHdheXMgPSBpZmVsc2UoaXMubmEoZGlzdGFuY2VfaGlnaHdheXMpLDAsZGlzdGFuY2VfaGlnaHdheXMpKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1DdmlsbGVNU0EpICsKICBnZW9tX3BvaW50KGRhdGE9aGlnaHdheVBvaW50c19maXNobmV0LCBhZXMoeD14eUMoaGlnaHdheVBvaW50c19maXNobmV0KVssMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhoaWdod2F5UG9pbnRzX2Zpc2huZXQpWywyXSwgCiAgICAgICAgICAgICAgICAgY29sb3VyPWZhY3RvcihudGlsZShkaXN0YW5jZV9oaWdod2F5cyw1KSkpLHNpemU9MS41KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoaGlnaHdheVBvaW50c19maXNobmV0LCJkaXN0YW5jZV9oaWdod2F5cyIpLDEsOCksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSJRdWludGlsZVxuQnJlYWtzIikgKwogIGdlb21fc2YoZGF0YT1DdmlsbGVIaWdod2F5cywgY29sb3VyID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIHRvIEhpZ2h3YXlzIiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzOyBIaWdod2F5cyB2aXN1YWxpemVkIGluIHJlZCIpICsKICBtYXBUaGVtZQpgYGAKCiMjIDIuNS4gQWRkIFNsb3BlIFZhcmlhYmxlCgpBIHJhc3RlciBkYXRhc2V0IG9mIHNsb3BlIGlzIGltcG9ydGVkIGFuZCBpdHMgZGF0YSBpcyBpbnRlZ3JhdGVkIGludG8gdGhlIGZpc2huZXQgdXNpbmcgJ2V4YWN0ZXh0cmFjdHInLgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UscmVzdWx0cyA9ICJoaWRlIn0KU2xvcGVSYXN0ZXIgPC0gcmFzdGVyKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vWWluZ3RvbmctWi9zcHJhd2wtZm9yZWNhc3RpbmcvOWY4MTBlYzhlMWU5Nzc0YzUxZDgzMTk0ZTIyODQ4ZTgyNDBkMjZmNy9kYXRhL0N2aWxsZV9TbG9wZS50aWYiKQoKIyBDaGVjayB0aGUgQ1JTIG9mIHRoZSByYXN0ZXIgYW5kIHRoZSBmaXNobmV0IGRhdGEKcmFzdGVyX2NycyA8LSBjcnMoU2xvcGVSYXN0ZXIpCmZpc2huZXRfY3JzIDwtIHN0X2NycyhmaXNobmV0KQoKIyBJZiBDUlMgZG9lc24ndCBtYXRjaCwgcmVwcm9qZWN0IHRoZSBmaXNobmV0IGRhdGEgdG8gbWF0Y2ggdGhlIHJhc3RlciBDUlMKaWYgKHJhc3Rlcl9jcnMgIT0gZmlzaG5ldF9jcnMpIHsKICBmaXNobmV0IDwtIHN0X3RyYW5zZm9ybShmaXNobmV0LCByYXN0ZXJfY3JzKX0KCiMgQ2FsY3VsYXRlIHRoZSBtZWFuIHJhc3RlciB2YWx1ZXMgd2l0aGluIGVhY2ggZmlzaG5ldCBjZWxsCm1lYW5fdmFsdWVzIDwtIGV4YWN0X2V4dHJhY3QoU2xvcGVSYXN0ZXIsIGZpc2huZXQsIGZ1biA9ICdtZWFuJykKCiMgQWRkIHRoZSBtZWFuIHZhbHVlcyBhcyBhIG5ldyBjb2x1bW4gdG8gdGhlIGZpc2huZXQgZGF0YQpmaXNobmV0JHNsb3BlIDwtIG1lYW5fdmFsdWVzCgojIFRyYW5zZm9ybSB0aGUgZmlzaG5ldCBiYWNrIEVQU0c6MjI4NApmaXNobmV0IDwtIHN0X3RyYW5zZm9ybShmaXNobmV0LCAyMjg0KQpgYGAKCiMjIDIuNi4gVGhlIFNwYXRpYWwgTGFnIG9mIERldmVsb3BtZW50CgpPdXIgbW9kZWwgaHlwb3RoZXNpemVzIHRoYXQgZGV2ZWxvcG1lbnQgZGVtYW5kIHBhcnRseSBkZXBlbmRzIG9uIGV4aXN0aW5nIGRldmVsb3BtZW50IHBhdHRlcm5zLiBBY2Nlc3NpYmlsaXR5IHBsYXlzIGEgc2lnbmlmaWNhbnQgcm9sZSBpbiB0cmFkaXRpb25hbCAnYmlkLXJlbnQnIGVjb25vbWljIG1vZGVscyBvZiBkZXZlbG9wbWVudC4gSG93ZXZlciwgdGhpcyBtb2RlbCBhc3N1bWVzIHNoYXJlZCBwcmVmZXJlbmNlcyBmb3IgY2VudHJhbCBjaXR5IGFjY2Vzcywgd2hpY2ggbWF5IG5vdCBob2xkIHRydWUgaW4gc3ByYXdsaW5nIHJlZ2lvbnMgbGlrZSBDaGFybG90dGVzdmlsbGUgTVNBLCB3aGVyZSBzdWJ1cmJhbiBsb2NhdGlvbnMgYXJlIGRlc2lyYWJsZS4KClRvIGZvcmVjYXN0IGdyb3d0aCwgZmVhdHVyZXMgbXVzdCBiZSBjcmVhdGVkIHRvIGFzc29jaWF0ZSB0aGVzZSBwYXR0ZXJucyB3aXRoIGRldmVsb3BtZW50LiBBY2Nlc3NpYmlsaXR5IGlzIG1lYXN1cmVkIHZpYSBzcGF0aWFsIGxhZywgaHlwb3RoZXNpemluZyB0aGF0IG5ldyBkZXZlbG9wbWVudCBkZXBlbmRzIG9uIGRpc3RhbmNlIHRvIGV4aXN0aW5nIGRldmVsb3BtZW50LiBUaGUgYXZlcmFnZSBkaXN0YW5jZSBmcm9tIGVhY2ggZ3JpZCBjZWxsIHRvIGl0cyB0d28gbmVhcmVzdCBkZXZlbG9wZWQgbmVpZ2hib3JpbmcgZ3JpZCBjZWxscyBpbiAyMDAxIGlzIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIG5uX2Z1bmN0aW9uLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIFRoZSBmdW5jdGlvbiBiZWxvdyBjYWxjdWxhdGVzIGF2ZXJhZ2UgbmVhcmVzdCBuZWlnaGJvciBkaXN0YW5jZSBiZXR3ZWVuIGsgcG9pbnQgbGF5ZXJzLiBUaGUgZmlyc3QgcGFyYW1ldGVyIHNwZWNpZmllcyBjb29yZGluYXRlcyB0aGF0IHdlIHdhbnQgdG8gYG1lYXN1cmVGcm9tYCwgaW4gdGhpcyBjYXNlLCBgZmlzaG5ldGAgY2VudHJvaWRzLiBUaGUgc2Vjb25kLCBpbmRpY2F0ZXMgdGhlIHBvaW50IGxheWVyIHdlIHdpc2ggdG8gYG1lYXN1cmVUb2AuCgpubl9mdW5jdGlvbiA8LSBmdW5jdGlvbihtZWFzdXJlRnJvbSxtZWFzdXJlVG8saykgewogICNjb252ZXJ0IHRoZSBzZiBsYXllcnMgdG8gbWF0cmljZXMKICBtZWFzdXJlRnJvbV9NYXRyaXggPC0KICAgIGFzLm1hdHJpeChtZWFzdXJlRnJvbSkKICBtZWFzdXJlVG9fTWF0cml4IDwtCiAgICBhcy5tYXRyaXgobWVhc3VyZVRvKQogIG5uIDwtICAgCiAgICBnZXQua25ueChtZWFzdXJlVG8sIG1lYXN1cmVGcm9tLCBrKSRubi5kaXN0CiAgICBvdXRwdXQgPC0KICAgIGFzLmRhdGEuZnJhbWUobm4pICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJ0aGlzUG9pbnQiKSAlPiUKICAgIGdhdGhlcihwb2ludHMsIHBvaW50X2Rpc3RhbmNlLCBWMTpuY29sKC4pKSAlPiUKICAgIGFycmFuZ2UoYXMubnVtZXJpYyh0aGlzUG9pbnQpKSAlPiUKICAgIGdyb3VwX2J5KHRoaXNQb2ludCkgJT4lCiAgICBzdW1tYXJpemUocG9pbnREaXN0YW5jZSA9IG1lYW4ocG9pbnRfZGlzdGFuY2UpKSAlPiUKICAgIGFycmFuZ2UoYXMubnVtZXJpYyh0aGlzUG9pbnQpKSAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KC10aGlzUG9pbnQpICU+JQogICAgcHVsbCgpCiAgCiAgcmV0dXJuKG91dHB1dCkgIAp9CmBgYAoKCk5leHQsIGxhZyBkaXN0YW5jZSBpcyBhcHBlbmRlZCB0byBvdXIgZmlzaG5ldC4gVGhlcmUgYXJlIDMgaW5wdXRzLiBUaGUgYGZpc2huZXRgIHdoaWNoIGlzIGNvbnZlcnRlZCB0byBhIGNvb3JkaW5hdGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBgeHlDYCBmdW5jdGlvbi4gMjAwMSBkZXZlbG9wZWQgYXJlYXMgYXJlIGNyZWF0ZWQgdXNpbmcgYGZpbHRlcmAuIFRoZSBtYXAgYmVsb3cgaWxsdXN0cmF0ZXMgcmVsYXRpdmUgYWNjZXNzaWJpbGl0eSBmcm9tIGV2ZXJ5IGdyaWQgY2VsbCB0byBuZWFyYnkgZGV2ZWxvcG1lbnQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmZpc2huZXQkbGFnRGV2ZWxvcG1lbnQgPC0KICAgIG5uX2Z1bmN0aW9uKHh5QyhmaXNobmV0KSwKICAgICAgICAgICAgICAgIHh5QyhmaWx0ZXIoYWdncmVnYXRlZFJhc3RlcnMsZGV2ZWxvcGVkPT0xKSksCiAgICAgICAgICAgICAgICAyKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1DdmlsbGVNU0EpICsKICBnZW9tX3BvaW50KGRhdGE9ZmlzaG5ldCwgCiAgICAgICAgICAgICBhZXMoeD14eUMoZmlzaG5ldClbLDFdLCB5PXh5QyhmaXNobmV0KVssMl0sIAogICAgICAgICAgICAgICAgIGNvbG91cj1mYWN0b3IobnRpbGUobGFnRGV2ZWxvcG1lbnQsNSkpKSwgc2l6ZT0wLjAxKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhmaXNobmV0LCJsYWdEZXZlbG9wbWVudCIpLDEsNyksCiAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlF1aW50aWxlXG5CcmVha3MiKSArCiAgbGFicyh0aXRsZSA9ICJTcGF0aWFsIExhZyB0byAyMDAxIERldmVsb3BtZW50IiwKICAgICAgIHN1YnRpdGxlID0gIkFzIGZpc2huZXQgY2VudHJvaWRzIikgKwogIG1hcFRoZW1lCmBgYAoKIyMgMi43LiBNU0EgQ291bnRpZXMKClRoZSBgdGlncmlzYCBwYWNrYWdlIGFsbG93cyBWaXJnaW5pYSBjb3VudHkgZ2VvbWV0cmllcyB0byBiZSBkb3dubG9hZGVkLiBBIHNwYXRpYWwgc3Vic2V0IHJldHVybnMgb25seSB0aGUgY291bnRpZXMgaW4gdGhlIE1TQS4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0Kb3B0aW9ucyh0aWdyaXNfY2xhc3MgPSAic2YiKQoKc3R1ZHlBcmVhQ291bnRpZXMgPC0gCiAgY291bnRpZXMoIlZpcmdpbmlhIikgJT4lCiAgc3RfdHJhbnNmb3JtKHN0X2NycyhDdmlsbGVNU0EpKSAlPiUKICBkcGx5cjo6c2VsZWN0KE5BTUUpICU+JQogIC5bc3RfYnVmZmVyKEN2aWxsZU1TQSwtMSksICwgb3A9c3RfaW50ZXJzZWN0c10gCmBgYAoKCiMjIDIuOC4gQ3JlYXRlIHRoZSBGaW5hbCBEYXRhc2V0CgpUaGUgbGFzdCBzdGVwIGlzIHRvIGJyaW5nIHRvZ2V0aGVyIGFsbCB0aGUgZGlzcGFyYXRlIGZlYXR1cmUgbGF5ZXJzIGludG8gYSBmaW5hbCBkYXRhc2V0IHRoYXQgY2FuIGJlIHVzZWQgZm9yIGFuYWx5c2lzLiBUaGUgdmFyaW91cyBmaXNobmV0IGxheWVycyBhcmUgYGNiaW5kYCB0b2dldGhlciwgbmVlZGVkIGZlYXR1cmVzIGFyZSBleHRyYWN0ZWQgYW5kIHRoZSBmaW5hbCBmaXNobmV0LCBgZGF0YCBpcyB0aGVuIGpvaW5lZCB3aXRoIGBzdHVkeUFyZWFDb3VudGllc2AgdG8gYXNzaWduIGVhY2ggZ3JpZCBjZWxsIHRvIGEgY291bnR5LiBgZGV2ZWxvcGVkMTlgIGlzIGNyZWF0ZWQgdG8gZGVzaWduYXRlIHRob3NlIGFyZWFzIHRoYXQgaGF2ZSBhbHJlYWR5IGJlZW4gZGV2ZWxvcGVkIHRocm91Z2ggMjAxOS4gRmluYWxseSwgYW55IGdyaWQgY2VsbCB0aGF0IGhhcyBhIGB3YXRlcmAgbGFuZCBjb3ZlciBkZXNpZ25hdGlvbiBpcyByZW1vdmVkLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXQgPC0gCiAgY2JpbmQoCiAgICBmaXNobmV0LCBoaWdod2F5UG9pbnRzX2Zpc2huZXQsZmlzaG5ldEdyYWR1YXRlMDAsCiAgICBmaXNobmV0SEh1bml0MDAsZmlzaG5ldFBvcHVsYXRpb24wMCxmaXNobmV0VmFjSEgwMCwKICAgIGZpc2huZXRXaGl0ZVAwMCxmaXNobmV0UG9wdWxhdGlvbjIwLGZpc2huZXRfcG9wX2NoYW5nZV8wMF8yMCxhZ2dyZWdhdGVkUmFzdGVycywKICAgIGZpc2huZXRISHVuaXQyMCwgZmlzaG5ldFdoaXRlUDIwKSAlPiUKICBkcGx5cjo6c2VsZWN0KGxjX2NoYW5nZSwgZGV2ZWxvcGVkLCBmb3Jlc3QsIGZhcm0sIHdldGxhbmRzLCBvdGhlclVuZGV2ZWxvcGVkLCBzbG9wZSx3YXRlciwKICAgICAgICAgICAgICAgIHRvdGFsX3BvcHVsYXRpb24sdG90YWxfZ3JhZHVhdGVfZGVncmVlX2hvbGRlcnMsdG90YWxfaG91c2luZ191bml0cywKICAgICAgICAgICAgICAgIHRvdGFsX3ZhY2FudF9ob3VzZWhvbGRzLHRvdGFsX3doaXRlLHRvdGFsX3BvcHVsYXRpb24yMDIwLHBvcF9DaGFuZ2VfMDBfMjAsZGlzdGFuY2VfaGlnaHdheXMsIGxhZ0RldmVsb3BtZW50LCB0b3RhbF9ob3VzaW5nX3VuaXRzMjAyMCwgdG90YWxfd2hpdGUyMDIwKSAlPiUKICBzdF9qb2luKHN0dWR5QXJlYUNvdW50aWVzKSAlPiUKICBtdXRhdGUoTkFNRSA9IGlmZWxzZShOQU1FID09ICJDaGFybG90dGVzdmlsbGUiLCAiQ2hhcmxvdHRlc3ZpbGxlIiwgIkFsYmVtYXJsZSIpKSAlPiUgCiAgbXV0YXRlKGRldmVsb3BlZDE5bGFnID0gaWZlbHNlKGxjX2NoYW5nZSA9PSAxICYgZGV2ZWxvcGVkID09IDEsIDAsIGRldmVsb3BlZCkpICU+JQogIGZpbHRlcih3YXRlciA9PSAwKSAKCnN0dWR5QXJlYUNvdW50aWVzIDwtIHN0dWR5QXJlYUNvdW50aWVzICU+JSAKICBmaWx0ZXIoTkFNRSA9PSAiQWxiZW1hcmxlIiB8IE5BTUUgPT0gIkNoYXJsb3R0ZXN2aWxsZSIpCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPXN0dWR5QXJlYUNvdW50aWVzLAogICAgICAgICAgYWVzKGZpbGwgPSBOQU1FKSkgKwogIGxhYnModGl0bGUgPSAiU3R1ZHkgQXJlYSBDb3VudGllcyIpICsKICBtYXBUaGVtZQpgYGAKCiMgMy4gRXhwbG9yYXRvcnkgQW5hbHlzaXMKCkluIHRoaXMgc2VjdGlvbiB3ZSBleHBsb3JlIHRoZSBleHRlbnQgdG8gd2hpY2ggZWFjaCBmZWF0dXJlIGlzIGFzc29jaWF0ZWQgd2l0aCBkZXZlbG9wbWVudCBjaGFuZ2UuIElmIHRoZSBnb2FsIHdhcyB0byBwcmVkaWN0IGEgY29udGludW91cyB2YXJpYWJsZSwgc2NhdHRlcnBsb3RzIGFuZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgbWFrZSB0aGlzIHByb2Nlc3Mgc3RyYWlnaHRmb3J3YXJkIGFuZCByZWxhdGl2ZWx5IGVhc3kgdG8gZXhwbGFpbiB0byBhIG5vbi10ZWNobmljYWwgZGVjaXNpb24gbWFrZXIuCgpJbiB0aGlzIGNhc2UgaG93ZXZlciwgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyBhIGJpbmFyeSBvdXRjb21lIC0gZWl0aGVyIGEgZ3JpZCBjZWxsIHdhcyBkZXZlbG9wZWQgYmV0d2VlbiAyMDAxIGFuZCAyMDE5IG9yIGl0IHdhc27igJl0LiBJbiB0aGlzIGNhc2UsIHRoZSByZWxldmFudCBxdWVzdGlvbiBpcyB3aGV0aGVyIGZvciBhIGdpdmVuIGZlYXR1cmUsIHRoZXJlIGlzIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gYXJlYXMgdGhhdCBjaGFuZ2VkIGFuZCBhcmVhcyB0aGF0IGRpZCBub3QuIFRoZXNlIGRpZmZlcmVuY2VzIGFyZSBleHBsb3JlZCBpbiBhIHNldCBvZiBwbG90cyBiZWxvdy4gRm9yIG1vZGVscyB3aXRoIGxvdHMgb2YgZmVhdHVyZXMsIHRoZXNlIHBsb3RzIGNvdWxkIGJlIGNvbXBsaW1lbnQgYnkgYSBzZXJpZXMgb2YgZGlmZmVyZW5jZSBpbiBtZWFucyBzdGF0aXN0aWNhbCB0ZXN0cy4KClRoZSBiZWxvdyBjb2RlIGJsb2NrIGBzZWxlY3RgcyB0aGUgaGlnaHdheXMgYW5kIHNwYXRpYWwgbGFnIGZlYXR1cmVzLCBjb252ZXJ0cyBlYWNoIHRvIGxvbmcgZm9ybSBhbmQgcGxvdHMgZWFjaCBhcyBiYXIgcGxvdHMuIE5vdGUgdGhhdCBgZ2VvbV9iYXJgIGNhbGN1bGF0ZXMgdGhlIGBtZWFuYC4gVGhlIG1lYW4gZGlzdGFuY2VfaGlnaHdheXMgaXMgc2lnbmlmaWNhbnRseSBsb3dlciBmb3IgdGhlIGBOZXcgRGV2ZWxvcG1lbnRgIGNhdGVnb3J5IGNvbXBhcmVkIHRvIHRoZSBgTm8gQ2hhbmdlYCBjYXRlZ29yeSwgaXQgaW5kaWNhdGVzIHRoYXQgbmV3IGRldmVsb3BtZW50cyB0ZW5kIHRvIGJlIGNsb3NlciB0byBoaWdod2F5cy4gT24gdGhlIG90aGVyIGhhbmQsIHRoZSBtZWFuIGBsYWdEZXZlbG9wbWVudGAgaXMgc2lnbmlmaWNhbnRseSBsb3dlciBmb3IgdGhlIGBOZXcgRGV2ZWxvcG1lbnRgIGNhdGVnb3J5IGNvbXBhcmVkIHRvIHRoZSBgTm8gQ2hhbmdlYCBjYXRlZ29yeSwgaXQgbWF5IGltcGx5IHRoYXQgbmV3IGRldmVsb3BtZW50IGlzIG1vcmUgbGlrZWx5IHRvIG9jY3VyIGluIGFyZWFzIHRoYXQgYXJlIG5lYXIgZXhpc3RpbmcgZGV2ZWxvcG1lbnQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdCAlPiUKICBkcGx5cjo6c2VsZWN0KGRpc3RhbmNlX2hpZ2h3YXlzLGxhZ0RldmVsb3BtZW50LGxjX2NoYW5nZSkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCBWYWx1ZSwgLWxjX2NoYW5nZSwgLWdlb21ldHJ5KSAlPiUKICBnZ3Bsb3QoLiwgYWVzKGxjX2NoYW5nZSwgVmFsdWUsIGZpbGw9bGNfY2hhbmdlKSkgKyAKICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuLnkgPSAibWVhbiIpICsKICAgIGZhY2V0X3dyYXAoflZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgICBsYWJzKHRpdGxlPSJOZXcgRGV2ZWxvcG1lbnQgYXMgYSBGdW5jdGlvbiBvZiB0aGUgQ29udGludW91cyBWYXJpYWJsZXMiKSArCiAgICBwbG90VGhlbWUgCmBgYAoKTmV4dCwgdGhlIHNhbWUgdmlzdWFsaXphdGlvbiBpcyBjcmVhdGVkIGZvciB0aGUgcG9wdWxhdGlvbiByZWxhdGVkIHZhcmlhYmxlcy4gVGhlIGhpZ2hlciBtZWFuIHZhbHVlcyBmb3IgYHRvdGFsX3BvcHVsYXRpb25gLCBgdG90YWxfcG9wdWxhdGlvbjIwMjBgLCBhbmQgYHBvcF9DaGFuZ2VfMDBfMjBgIGluIHRoZSBgTmV3IERldmVsb3BtZW50YCBjYXRlZ29yeSBzdWdnZXN0IHRoYXQgZGV2ZWxvcG1lbnQgaXMgbW9yZSBsaWtlbHkgdG8gb2NjdXIgaW4gYXJlYXMgd2l0aCBoaWdoZXIgcG9wdWxhdGlvbnMgYW5kIGdyZWF0ZXIgcG9wdWxhdGlvbiBncm93dGguIAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXQgJT4lCiAgZHBseXI6OnNlbGVjdCh0b3RhbF9wb3B1bGF0aW9uLHRvdGFsX3BvcHVsYXRpb24yMDIwLHBvcF9DaGFuZ2VfMDBfMjAsbGNfY2hhbmdlKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtbGNfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQogIGdncGxvdCguLCBhZXMobGNfY2hhbmdlLCBWYWx1ZSwgZmlsbD1sY19jaGFuZ2UpKSArCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAic3VtbWFyeSIsIGZ1bi55ID0gIm1lYW4iKSArCiAgICBmYWNldF93cmFwKH5WYXJpYWJsZSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiTmV3IERldmVsb3BtZW50IiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgICBsYWJzKHRpdGxlPSJOZXcgRGV2ZWxvcG1lbnQgYXMgYSBGdW5jdGlvbiBvZiBGYWN0b3IgVmFyaWFibGVzIikgKwogICAgcGxvdFRoZW1lCmBgYAoKTmV4dCwgYSB0YWJsZSBvZiBsYW5kIGNvdmVyIGNvbnZlcnNpb24gYmV0d2VlbiAyMDAxIGFuZCAyMDE5IGlzIGNyZWF0ZWQuIFRoZSB0YWJsZSBzdWdnZXN0cyBmb3IgaW5zdGFuY2UsIHRoYXQgMC43MSUgb2YgVGhlIHBsb3RzIGFib3ZlIHN1Z2dlc3QgdGhhdCB0aGUgY29udGludW91cyB2YXJpYWJsZSAoZS5nLiwgYGRpc3RhbmNlX2hpZ2h3YXlzYCwgYGxhZ0RldmVsb3BtZW50YCwgcG9wdWxhdGlvbiByZWxhdGVkIHZhcmlhYmxlcykgaGFzIGFuIGFzc29jaWF0aW9uIHdpdGggdGhlIG9jY3VycmVuY2Ugb2YgbmV3IGRldmVsb3BtZW50LgoKCk5leHQsIGEgdGFibGUgb2YgbGFuZCBjb3ZlciBjb252ZXJzaW9uIGJldHdlZW4gMjAwMSBhbmQgMjAxOSBpcyBjcmVhdGVkLiBUaGUgdGFibGUgc3VnZ2VzdHMgZm9yIGluc3RhbmNlLCB0aGF0IDAuNzclIG9mIGZhcm1sYW5kIHJlZ2lvbmFsbHkgd2FzIGNvbnZlcnRlZCB0byBkZXZlbG9wbWVudCBiZXR3ZWVuIDIwMDEgYW5kIDIwMTkuIAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXQgJT4lCiAgZHBseXI6OnNlbGVjdChsY19jaGFuZ2U6b3RoZXJVbmRldmVsb3BlZCxkZXZlbG9wZWQpICU+JQogIGdhdGhlcihMYW5kX0NvdmVyX1R5cGUsIFZhbHVlLCAtbGNfY2hhbmdlLCAtZ2VvbWV0cnkpICU+JQogICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgICAgZ3JvdXBfYnkobGNfY2hhbmdlLCBMYW5kX0NvdmVyX1R5cGUpICU+JQogICAgIHN1bW1hcml6ZShuID0gc3VtKGFzLm51bWVyaWMoVmFsdWUpKSkgJT4lCiAgICAgdW5ncm91cCgpICU+JQogICAgbXV0YXRlKENvbnZlcnNpb25fUmF0ZSA9IHBhc3RlMChyb3VuZCgxMDAgKiBuL3N1bShuKSwgMiksICIlIikpICU+JQogICAgZmlsdGVyKGxjX2NoYW5nZSA9PSAxKSAlPiUKICBkcGx5cjo6c2VsZWN0KExhbmRfQ292ZXJfVHlwZSxDb252ZXJzaW9uX1JhdGUpICU+JQogIGthYmxlKCkgJT4lIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYpCmBgYAoKCiMgNC4gUHJlZGljdGluZyBmb3IgMjAxMAoKSW4gdGhpcyBzZWN0aW9uLCBzaXggc2VwYXJhdGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgYXJlIGVzdGltYXRlZCB0byBwcmVkaWN0IGRldmVsb3BtZW50IGNoYW5nZSBiZXR3ZWVuIDIwMDEgYW5kIDIwMTkgLSB3aXRoIGVhY2ggc3Vic2VxdWVudCBtb2RlbCBtb3JlIHNvcGhpc3RpY2F0ZWQgdGhlbiB0aGUgbGFzdC4gVG8gZG8gc28sIHRoZSBkYXRhIGlzIHNwbGl0IGludG8gNTAlIHRyYWluaW5nL3Rlc3Qgc2V0cy4gTW9kZWxzIGFyZSBlc3RpbWF0ZWQgb24gdGhlIHRyYWluaW5nIHNldC4KCkZvciBicmV2aXR5LCBhIGxlc3Mgc29waGlzdGljYXRlZCBhcHByb2FjaCB0byBkZXNjcmliaW5nIHRoZSBhY2N1cmFjeSBhbmQgZ2VuZXJhbGl6YWJpbGl0eSBvZiBwcmVkaWN0aW9ucyBmb3IgZWFjaCBtb2RlbCBpcyB0YWtlbiBoZXJlLCBqdWRnaW5nIGVhY2ggYnkgdGhlIE1jRmFkZGVuIG9yIOKAnFBzdWVkb+KAnSBSIFNxdWFyZWQgc3RhdGlzdGljIG9uIHRoZSB0ZXN0IHNldC4gVGhlIG1vZGVsIHdpdGggdGhlIGdyZWF0ZXN0IGdvb2RuZXNzIG9mIGZpdCBpcyB0aGVuIHVzZWQgZm9yIHRoZSBwdXJwb3NlcyBvZiBwcmVkaWN0aW9uLgoKIyMgNC4xLiBNb2RlbGluZwoKRmlyc3QsIGBkYXRgIGlzIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4gTm90ZSBob3cgaW1iYWxhbmNlZCB0aGUgcGFuZWwgaXMgd2l0aCBgdGFibGUoZGF0VHJhaW4kbGNfY2hhbmdlMSlgLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpzZXQuc2VlZCgzNDU2KQp0cmFpbkluZGV4IDwtIAogIGNyZWF0ZURhdGFQYXJ0aXRpb24oZGF0JGRldmVsb3BlZCwgcCA9IC41MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzID0gMSkKZGF0VHJhaW4gPC0gZGF0WyB0cmFpbkluZGV4LF0KZGF0VGVzdCAgPC0gZGF0Wy10cmFpbkluZGV4LF0KCm5yb3coZGF0KQpgYGAKCk5leHQgc2l4IHNlcGFyYXRlIGBnbG1gIG1vZGVscyBhcmUgZXN0aW1hdGVkIGFkZGluZyBuZXcgdmFyaWFibGVzIGZvciBlYWNoLiBGaWd1cmUgNC4xIHNob3dzIHRoZSBQc3VlZG8gUi1TcXVhcmVkIGFzc29jaWF0ZWQgd2l0aCBlYWNoIG1vZGVsLgoKYE1vZGVsMWAgaW5jbHVkZXMgb25seSB0aGUgMjAwMSBsYW5kIGNvdmVyIHR5cGVzLiBgTW9kZWwyYCBhZGRzIHRoZSBgbGFnRGV2ZWxvcG1lbnRgLiBNb2RlbHMgMywgNCBhbmQgNSBhdHRlbXB0IHRocmVlIGRpZmZlcmVudCBhcHByb2FjaGVzIGZvciBtb2RlbGluZyBkZW1vZ3JhcGhpYyBjaGFuZ2VzLCBpbmZyYXN0cnVjdHVyZSBhbmQgc2xvcGU7IGBNb2RlbDNgIHVzZXMgMjAwMCBwb3B1bGF0aW9uIGFuZCBkaXN0YW5jZSB0byBoaWdod2F5LCBgTW9kZWw0YCBhZGRzIHNsb3BlIHRvIE1vZGVsMywgYW5kIGBNb2RlbDVgIGFkZHMgcG9wdWxhdGlvbiBjaGFuZ2UgdG8gTW9kZWw0LiBgTW9kZWw2YCBhZGRzIGRlbW9ncmFwaGljIGZlYXR1cmVzIHRvIE1vZGVsNS4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KTW9kZWwxIDwtIGdsbShsY19jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCwgCiAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKQoKTW9kZWwyIDwtIGdsbShsY19jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50LCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pCiAgICAgICAgICAgICAgCk1vZGVsMyA8LSBnbG0obGNfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIHRvdGFsX3BvcHVsYXRpb24gKwogICAgICAgICAgICAgICAgIGRpc3RhbmNlX2hpZ2h3YXlzLCAKICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gZGF0VHJhaW4pICAKCk1vZGVsNCA8LSBnbG0obGNfY2hhbmdlIH4gd2V0bGFuZHMgKyBmb3Jlc3QgICsgZmFybSArIG90aGVyVW5kZXZlbG9wZWQgKyBsYWdEZXZlbG9wbWVudCArIHRvdGFsX3BvcHVsYXRpb24gKwogICAgICAgICAgICAgICAgIGRpc3RhbmNlX2hpZ2h3YXlzICsgc2xvcGUsIAogICAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgCgpNb2RlbDUgPC0gZ2xtKGxjX2NoYW5nZSB+IHdldGxhbmRzICsgZm9yZXN0ICArIGZhcm0gKyBvdGhlclVuZGV2ZWxvcGVkICsgbGFnRGV2ZWxvcG1lbnQgKyB0b3RhbF9wb3B1bGF0aW9uICsKICAgICAgICAgICAgICAgICBkaXN0YW5jZV9oaWdod2F5cyArIHNsb3BlICsgcG9wX0NoYW5nZV8wMF8yMCArIHRvdGFsX3BvcHVsYXRpb24yMDIwLAogICAgICAgICAgICAgICAgZmFtaWx5PSJiaW5vbWlhbCIobGluaz0ibG9naXQiKSwgZGF0YSA9IGRhdFRyYWluKSAgICAgICAgICAgICAgIAoKTW9kZWw2IDwtIGdsbShsY19jaGFuZ2UgfiB3ZXRsYW5kcyArIGZvcmVzdCAgKyBmYXJtICsgb3RoZXJVbmRldmVsb3BlZCArIGxhZ0RldmVsb3BtZW50ICsKICAgICAgICAgICAgICAgICBkaXN0YW5jZV9oaWdod2F5cyArIHNsb3BlICsgcG9wX0NoYW5nZV8wMF8yMAogICAgICAgICAgICAgICAgICsgdG90YWxfaG91c2luZ191bml0cyAgKyB0b3RhbF93aGl0ZSwKICAgICAgICAgICAgICAgIGZhbWlseT0iYmlub21pYWwiKGxpbms9ImxvZ2l0IiksIGRhdGEgPSBkYXRUcmFpbikKYGBgCgpCZWxvdyBjb2RlcyBjcmVhdGUgYSBkYXRhIGZyYW1lIG9mIHBzdWRlbyBSIFNxdWFyZXMgZm9yIGVhY2ggbW9kZWwgYW5kIHBsb3R0aW5nIHRoZW0gZm9yIGNvbXBhcmlzb24uIFRoaXMgYXBwcm9hY2ggbG9vcHMgdGhyb3VnaCB0aGUgbW9kZWxzIHJldHJpZXZpbmcgdGhlIGdvb2RuZXNzIG9mIGZpdCBmb3IgZWFjaC4gYE1vZGVsNmAgaXMgdGhlIGZpbmFsIG1vZGVsIGVtcGxveWVkIGZvciBwcmVkaWN0aW9uLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQptb2RlbExpc3QgPC0gcGFzdGUwKCJNb2RlbCIsIDE6NikKbWFwX2RmYyhtb2RlbExpc3QsIGZ1bmN0aW9uKHgpcFIyKGdldCh4KSkpWzQsXSAlPiUKICBzZXROYW1lcyhwYXN0ZTAoIk1vZGVsIiwxOjYpKSAlPiUKICBnYXRoZXIoTW9kZWwsTWNGYWRkZW4pICU+JQogIGdncGxvdChhZXMoTW9kZWwsTWNGYWRkZW4pKSArCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICAgIGxhYnModGl0bGU9ICJNY0ZhZGRlbiBSLVNxdWFyZWQgYnkgTW9kZWwiKSArCiAgICBwbG90VGhlbWUKYGBgCgpOZXh0LCBhIGRhdGEgZnJhbWUgaXMgY3JlYXRlZCB0aGF0IGluY2x1ZGVzIGNvbHVtbnMgZm9yIHRoZSBvYnNlcnZlZCBkZXZlbG9wbWVudCBjaGFuZ2UsIGBsY19jaGFuZ2VgLCBhbmQgb25lIHRoYXQgaW5jbHVkZXMgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIGBNb2RlbDZgLiBUaGlzIGRhdGEgZnJhbWUgaXMgdGhlbiB1c2VkIGFzIGFuIGlucHV0IHRvIGEgZGVuc2l0eSBwbG90IHZpc3VhbGl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYnkgb2JzZXJ2ZWQgY2xhc3MuIE9ubHkgYSBzbWFsbCBudW1iZXIgb2YgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYXJlIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA1MCUgYChucm93KGZpbHRlcih0ZXN0U2V0UHJvYnMsIHByb2JzID49IC41MCkpIC8gbnJvdyhkYXRUZXN0KSlgLiBUaGlzIG1ha2VzIGdvb2Qgc2Vuc2UsIGdpdmVuIGhvdyByYXJlIG9mIGFuIGV2ZW50IGRldmVsb3BtZW50IGlzIGluIG91ciBkYXRhc2V0LiBVbHRpbWF0ZWx5LCBpbiBvcmRlciB0byBqdWRnZSBvdXIgbW9kZWwgd2l0aCBhIGNvbmZ1c2lvbiBtYXRyaXgsIGEgc21hbGxlciBkZXZlbG9wbWVudCBjbGFzc2lmaWNhdGlvbiB0aHJlc2hvbGQgbXVzdCBiZSBlbXBsb3llZC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KdGVzdFNldFByb2JzIDwtIAogIGRhdGEuZnJhbWUoY2xhc3MgPSBkYXRUZXN0JGxjX2NoYW5nZSwKICAgICAgICAgICAgIHByb2JzID0gcHJlZGljdChNb2RlbDYsIGRhdFRlc3QsIHR5cGU9InJlc3BvbnNlIikpIAogIApnZ3Bsb3QodGVzdFNldFByb2JzLCBhZXMocHJvYnMpKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhmaWxsPWNsYXNzKSwgYWxwaGE9MC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTEsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gb2YgdGVzdCBzZXQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMiLAogICAgICAgeD0iUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiLHk9IkRlbnNpdHkiKSArCiAgcGxvdFRoZW1lCmBgYAoKIyMgNC4yLiBBY2N1cmFjeQoKTm93IHRvIHBpY2sgYSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgdGhyZXNob2xkIHRvIGNsYXNzaWZ5IGFuIGFyZWEgYXMgaGF2aW5nIG5ldyBkZXZlbG9wbWVudC4gKlNlbnNpdGl2aXR5KiBvciB0aGUgVHJ1ZSBQb3NpdGl2ZSByYXRlIGlzIHRoZSBwcm9wb3J0aW9uIG9mIGFjdHVhbCBwb3NpdGl2ZXMgKDHigJlzKSB0aGF0IHdlcmUgcHJlZGljdGVkIHRvIGJlIHBvc2l0aXZlLiBGb3IgZXhhbXBsZSwgdGhlIFNlbnNpdGl2aXR5IGluIG91ciBtb2RlbCBpcyB0aGUgcmF0ZSBvZiBkZXZlbG9wZWQgYXJlYXMgYWN0dWFsbHkgcHJlZGljdGVkIGFzIHN1Y2guICpTcGVjaWZpY2l0eSogb3IgVHJ1ZSBOZWdhdGl2ZSBSYXRlIGlzIHRoZSBwcm9wb3J0aW9uIG9mIGFjdHVhbCBuZWdhdGl2ZXMgKDDigJlzKSB0aGF0IHdlcmUgcHJlZGljdGVkIHRvIGJlIG5lZ2F0aXZlcy4gRm9yIGV4YW1wbGUsIHRoZSBTcGVjaWZpY2l0eSBpbiBvdXIgbW9kZWwgaXMgdGhlIHJhdGUgb2YgTm8gQ2hhbmdlIGFyZWFzIHRoYXQgd2VyZSBjb3JyZWN0bHkgcHJlZGljdGVkIGFzIE5vIGNoYW5nZS4KClRoZXJlIGFyZSBzb21lIGNsZWFyIHRyYWRlLW9mZnMgYmV0d2VlbiBTZW5zaXRpdml0eSBhbmQgU3BlY2lmaWNpdHkgaW4gb3VyIG1vZGVsIHRoYXQgZGVzZXJ2ZSBzb21lIGV4cGxvcmF0aW9uLiBUbyBpbGx1c3RyYXRlLCB0d28gZGlmZmVyZW50IHRocmVzaG9sZHMgb2YgMTMlIGFuZCAzMCUgYXJlIGV4cGxvcmVkLiBQcmVkaWN0ZWQgY2xhc3NlcyBmb3IgYm90aCB0aHJlc2hvbGRzIGFyZSBnZW5lcmF0ZWQgYW5kIGluc3RlYWQgb2YgdXNpbmcgdGhlIGBjb25mdXNpb25NYXRyaXhgIGZ1bmN0aW9uIGZyb20gYGNhcmV0YCBhcyB3ZSBoYXZlIGluIHRoZSBwYXN0LCBoZXJlIGNvbmZ1c2lvbiBtYXRyaXggbWV0cmljcyBhcmUgZGVyaXZlZCBmcm9tIHRoZSBgeWFyZHN0aWNrYCBwYWNrYWdlLiBUaGlzIGFsbG93cyB1cyB0byBgZ3JvdXBfYnlgIHRoZSB0aHJlc2hvbGQgYW5kIGBzdW1tYXJpemVgIHRoZSBtZXRyaWNzIG9mIGludGVyZXN0LgoKVGhlIGBvcHRpb25zYCBjYWxsIGJlbG93IGlzIHJlcXVpcmVkIHRvIHRlbGwgYHlhcmRzdGlja2AgdGhhdCB0aGUgcG9zaXRpdmUgZmFjdG9yIGNsYXNzIGluIGB0ZXN0U2V0UHJvYnNgIGlzIGAxYC4gV2l0aG91dCBpdCwgeWFyZHN0aWNrIHdpbGwgYnkgZGVmYXVsdCBzZWUgdGhlIGZpcnN0IGZhY3RvciBsZXZlbCBhcyBgMGAgYW5kIGZsaXAgdGhlIGNvbmZ1c2lvbiBtZXRyaWNzIGFyb3VuZC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kb3B0aW9ucyh5YXJkc3RpY2suZXZlbnRfZmlyc3QgPSBGQUxTRSkKCnRlc3RTZXRQcm9icyA8LSAKICB0ZXN0U2V0UHJvYnMgJT4lIAogIG11dGF0ZShwcmVkQ2xhc3NfMDUgPSBhcy5mYWN0b3IoaWZlbHNlKHRlc3RTZXRQcm9icyRwcm9icyA+PSAwLjA1ICwxLDApKSwKICAgICAgICAgcHJlZENsYXNzXzIwID0gYXMuZmFjdG9yKGlmZWxzZSh0ZXN0U2V0UHJvYnMkcHJvYnMgPj0gMC4yICwxLDApKSkgCgp0ZXN0U2V0UHJvYnMgJT4lCiAgZHBseXI6OnNlbGVjdCgtcHJvYnMpICU+JQogIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1jbGFzcykgJT4lCiAgZ3JvdXBfYnkoVmFyaWFibGUpICU+JQogIHN1bW1hcml6ZShTZW5zaXRpdml0eSA9IHJvdW5kKHlhcmRzdGljazo6c2Vuc192ZWMoY2xhc3MsZmFjdG9yKFZhbHVlKSksMiksCiAgICAgICAgICAgIFNwZWNpZmljaXR5ID0gcm91bmQoeWFyZHN0aWNrOjpzcGVjX3ZlYyhjbGFzcyxmYWN0b3IoVmFsdWUpKSwyKSwKICAgICAgICAgICAgQWNjdXJhY3kgPSByb3VuZCh5YXJkc3RpY2s6OmFjY3VyYWN5X3ZlYyhjbGFzcyxmYWN0b3IoVmFsdWUpKSwyKSkgJT4lIAogIGthYmxlKCkgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRikKYGBgCgpUaGUgMTMlIHRocmVzaG9sZCBjb3JyZWN0bHkgcHJlZGljdHMgYSBoaWdoZXIgbnVtYmVyIG9mIG5ldyBkZXZlbG9wbWVudCBhcmVhcyAoU2Vuc2l0aXZpdHkpLCBidXQgaW5jb3JyZWN0bHkgcHJlZGljdHMgYSBsb3dlciBudW1iZXIgb2Ygbm8gY2hhbmdlIGFyZWFzIChTcGVjaWZpY2l0eSkuIEFzIHRoZXJlIGFyZSBmYXIgbW9yZSBubyBjaGFuZ2UgYXJlYXMgaW4gdGhlIGRhdGEsIHRoaXMgaXMgcmVmbGVjdGVkIGluIGEgbG93ZXIgb3ZlcmFsbCBhY2N1cmFjeS4gQ29udmVyc2VseSwgdGhlIDMwJSB0aHJlc2hvbGQgaGFzIGEgbG93ZXIgU2Vuc2l0aXZpdHkgcmF0ZSBhbmQgYnV0IGEgZmFyIGhpZ2hlciBTcGVjaWZpY2l0eSByYXRlLiBBZ2FpbiwgYmVjYXVzZSBvZiB0aGUgZGF0YXNldCBpcyBtYWpvcml0eSBubyBjaGFuZ2UgYXJlYXMsIHRoaXMgbGVhZHMgdG8gYSBmYXIgaGlnaGVyIEFjY3VyYWN5IHJhdGUuCgpHaXZlbiB0aGUgdXNlIGNhc2UsIGFuZCB0aGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgbGFuZCBjb3ZlciBjaGFuZ2UsIGl0IG1heSBiZSBtb3JlIHVzZWZ1bCB0byBoYXZlIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyBnZW5lcmFsbHkgd2hlcmUgbmV3IGRldmVsb3BtZW50IG9jY3VycyByYXRoZXIgdGhhbiBvbmUgdGhhdCBwcmVkaWN0cyBwcmVjaXNlbHkgd2hlcmUuIEFzIGlsbHVzdHJhdGVkIGJlbG93LCB0aGUgMjAlIHRocmVzaG9sZCBwcm92aWRlcyB0aGlzIG91dGNvbWUuIFRoZXNlIHRyYWRlLW9mZnMgY2FuIGJlIHZpc3VhbGl6ZWQgaW4gdGhlIHBsb3QgYmVsb3cuIEhlcmUgdGhlIG1vZGVsIGlzIHVzZWQgdG8gcHJlZGljdCBmb3IgdGhlIGVudGlyZSBgZGF0YCBkYXRhc2V0LiAyMCUgdGhyZXNob2xkIGxvb2tzIG1vcmUgcmVhc29uYWJsZSBnaXZlbiB0aGUgZGlzdHJpYnV0aW9uIG9mIG9ic2VydmVkIGRldmVsb3BtZW50IGNoYW5nZS4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KcHJlZHNGb3JNYXAgPC0gICAgICAgICAKICBkYXQgJT4lCiAgICBtdXRhdGUocHJvYnMgPSBwcmVkaWN0KE1vZGVsNiwgZGF0LCB0eXBlPSJyZXNwb25zZSIpICwKICAgICAgICAgICBUaHJlc2hvbGRfNV9QY3QgPSBhcy5mYWN0b3IoaWZlbHNlKHByb2JzID49IDAuMDUgLDEsMCkpLAogICAgICAgICAgIFRocmVzaG9sZF8yMF9QY3QgPSAgYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjIwICwxLDApKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGxjX2NoYW5nZSxUaHJlc2hvbGRfNV9QY3QsVGhyZXNob2xkXzIwX1BjdCkgJT4lCiAgICBnYXRoZXIoVmFyaWFibGUsVmFsdWUsIC1nZW9tZXRyeSkgJT4lCiAgICBzdF9jYXN0KCJQT0xZR09OIikKYGBgCgoKPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoPSA4fQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhPXByZWRzRm9yTWFwLCBhZXMoeD14eUMocHJlZHNGb3JNYXApWywxXSwgeT14eUMocHJlZHNGb3JNYXApWywyXSwgY29sb3VyPVZhbHVlKSkgKwogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIk5vIENoYW5nZSIsIk5ldyBEZXZlbG9wbWVudCIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZT0iIikgKwogIGxhYnModGl0bGU9IkRldmVsb3BtZW50IFByZWRpY3Rpb25zIC0gTG93IFRocmVzaG9sZCIpICsgCiAgbWFwVGhlbWUKYGBgCjwvZGl2PgoKVG8gcHJvdmlkZSBhIGJpdCBtb3JlIGluc2lnaHQsIHRoZSBjb2RlIGJsb2NrIGJlbG93IHByb2R1Y2VzIGJvdGggdHJ1ZSBwb3NpdGl2ZXMgKFNlbnNpdGl2aXR5KSBhbmQgdHJ1ZSBuZWdhdGl2ZXMgKFNwZWNpZmljaXR5KSBmb3IgZWFjaCBncmlkIGNlbGwgYnkgdGhyZXNob2xkIHR5cGUuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CkNvbmZ1c2lvbk1hdHJpeC5tZXRyaWNzIDwtCiAgZGF0ICU+JQogICAgbXV0YXRlKHByb2JzID0gcHJlZGljdChNb2RlbDMsIGRhdCwgdHlwZT0icmVzcG9uc2UiKSAsCiAgICAgICAgICAgVGhyZXNob2xkXzVfUGN0ID0gYXMuZmFjdG9yKGlmZWxzZShwcm9icyA+PSAwLjA1ICwxLDApKSwKICAgICAgICAgICBUaHJlc2hvbGRfMjBfUGN0ID0gIGFzLmZhY3RvcihpZmVsc2UocHJvYnMgPj0gMC4yMCAsMSwwKSkpICU+JQogICAgbXV0YXRlKFRydWVQXzA1ID0gaWZlbHNlKGxjX2NoYW5nZSAgPT0gMSAmIFRocmVzaG9sZF81X1BjdCA9PSAxLCAxLDApLAogICAgICAgICAgIFRydWVOXzA1ID0gaWZlbHNlKGxjX2NoYW5nZSAgPT0gMCAmIFRocmVzaG9sZF81X1BjdCA9PSAwLCAxLDApLAogICAgICAgICAgIFRydWVQXzIwID0gaWZlbHNlKGxjX2NoYW5nZSAgPT0gMSAmIFRocmVzaG9sZF8yMF9QY3QgPT0gMSwgMSwwKSwKICAgICAgICAgICBUcnVlTl8yMCA9IGlmZWxzZShsY19jaGFuZ2UgID09IDAgJiBUaHJlc2hvbGRfMjBfUGN0ID09IDAsIDEsMCkpICU+JQogICAgZHBseXI6OnNlbGVjdCguLCBzdGFydHNfd2l0aCgiVHJ1ZSIpKSAlPiUKICAgIGdhdGhlcihWYXJpYWJsZSwgVmFsdWUsIC1nZW9tZXRyeSkgJT4lCiAgICBzdF9jYXN0KCJQT0xZR09OIikgCmBgYAoKPGRpdiBjbGFzcz0ic3VwZXJiaWdpbWFnZSI+CmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSA4IH0KZ2dwbG90KGRhdGE9Q29uZnVzaW9uTWF0cml4Lm1ldHJpY3MpICsKICBnZW9tX3BvaW50KGFlcyh4PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDFdLCAKICAgICAgICAgICAgICAgICB5PXh5QyhDb25mdXNpb25NYXRyaXgubWV0cmljcylbLDJdLCBjb2xvdXIgPSBhcy5mYWN0b3IoVmFsdWUpKSkgKwogIGZhY2V0X3dyYXAoflZhcmlhYmxlKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwgbGFiZWxzPWMoIkNvcnJlY3QiLCJJbmNvcnJlY3QiKSwKICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iRGV2ZWxvcG1lbnQgUHJlZGljdGlvbnMgLSBMb3cgVGhyZXNob2xkIikgKyBtYXBUaGVtZQpgYGAKPC9kaXY+CgoKIyA1LiBQcmVkaWN0aW5nIExhbmQgQ292ZXIgRGVtYW5kIGZvciAyMDQwCgojIyA1LjEuIFVwZGF0ZSBQb3B1bGF0aW9uIENoYW5nZSBhbmQgTGFnIERldmVsb3BtZW50IHZhbHVlcwoKQXQgdGhpcyBwb2ludCwgYSBzaW1wbGUgYnV0IHVzZWZ1bCBtb2RlbCBoYXMgYmVlbiB0cmFpbmVkIHRvIHByZWRpY3QgdXJiYW4gZGV2ZWxvcG1lbnQgYmV0d2VlbiAyMDAxIGFuZCAyMDE5IGFzIGEgZnVuY3Rpb24gb2YgYmFzZWxpbmUgZmVhdHVyZXMgZnJvbSAyMDAxIGluY2x1ZGluZyBsYW5kIGNvdmVyLCBidWlsdCBlbnZpcm9ubWVudCBhbmQgcG9wdWxhdGlvbi4gTmV4dCwgd2UgYXJlIGdvaW5nIHRvIHVwZGF0ZSBvdXIgZmVhdHVyZXMgdG8gcmVmbGVjdCBhIDIwMTkgYmFzZWxpbmUuIEhhdmluZyBkb25lIHNvLCBwcmVkaWN0aW9ucyBmcm9tIG91ciBuZXcgbW9kZWwgd291bGQgdGhlbiBiZSBmb3IgMjA0MC4KCkZvciBicmV2aXR5LCB3ZSBvbmx5IHVwZGF0ZSB0d28gZmVhdHVyZXMgaW4gb3VyIG1vZGVsIGZvciBub3cuIEZpcnN0LCBwb3B1bGF0aW9uIGNoYW5nZSAoYHBvcF9jaGFuZ2VgKSBpcyB1cGRhdGVkIHVzaW5nIGNvdW50eSBsZXZlbCBwb3B1bGF0aW9uIHByb2plY3Rpb25zIHZpc3VhbGl6ZWQgaW4gdGhlIHBsb3QgYmVsb3cuIFRoZSBzZWNvbmQgaXMgYGxhZ0RldmVsb3BtZW50YCwgd2hpY2ggZGVzY3JpYmVzIGhvdyBwcmVkaWN0ZWQgbmV3IGRldmVsb3BtZW50IHJlbGF0ZXMgaW4gc3BhY2UgdG8gb2xkIGRldmVsb3BtZW50LgoKT25jZSB0aGUgZmVhdHVyZXMgYXJlIHVwZGF0ZWQsIDIwNDAgcHJlZGljdGlvbnMgYXJlIGVzdGltYXRlZCBhbmQgbWFwcGVkLgoKQmVsb3csIGBsYWdEZXZlbG9wbWVudGAgaXMgbXV0YXRlZCB0byBkZXNjcmliZSBhdmVyYWdlIGRpc3RhbmNlIHRvIDIwMTkgZGV2ZWxvcG1lbnQuIE5vdGUgdGhhdCB0aGUgZmllbGQgbmFtZSwgYGxhZ0RldmVsb3BtZW50YCBpcyB1bmNoYW5nZWQgKGllLiBub3QgdXBkYXRlZCB0byBgbGFnRGV2ZWxvcG1lbnRfMjAxOWApLiBUaGlzIGlzIGRvbmUgcHVycG9zZWZ1bGx5IGFzIG1vZGVsNiBoYXMgYSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGNhbGxlZCBgbGFnRGV2ZWxvcG1lbnRgLiBJZiB0aGlzIHZhcmlhYmxlIHdhc27igJl0IHByZXNlbnQgaW4gb3VyIHVwZGF0ZWQgZGF0YSBmcmFtZSB0aGVuIHRoZSBgcHJlZGljdGAgY29tbWFuZCB3b3VsZCBmYWlsLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXQgPC0KICBkYXQgJT4lCiAgbXV0YXRlKGxhZ0RldmVsb3BtZW50ID0gbm5fZnVuY3Rpb24oeHlDKC4pLCB4eUMoZmlsdGVyKC4sZGV2ZWxvcGVkMTlsYWcgPT0gMikpLDIpKQpgYGAKCk5vdyB0byB1cGRhdGUgcG9wdWxhdGlvbiBjaGFuZ2UuIEEgbmV3IGRhdGEgZnJhbWUsIGBjb3VudHlQb3B1bGF0aW9uXzIwNDBgIGlzIGNyZWF0ZWQgd2hpY2ggaW5jbHVkZXMgMjAyMCBwb3B1bGF0aW9uIGNvdW50cyBhbmQgMjA0MCBwcm9qZWN0aW9ucyBmb3IgZWFjaCBjb3VudHkgaW4gdGhlIHN0dWR5IGFyZWEuIFBvcHVsYXRpb24gaXMgcGxvdHRlZCBieSB5ZWFyIGFuZCBieSBjb3VudHkuIAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpjb3VudHlQb3B1bGF0aW9uXzIwNDAgPC0gCiAgZGF0YS5mcmFtZSgKICAgTkFNRSA9IAogICAgIGMoIkFsYmVtYXJsZSIsIkNoYXJsb3R0ZXN2aWxsZSIpLAogICBjb3VudHlfcHJvamVjdGlvbl8yMDQwID0gCiAgICAgYygxMzg1MjMsNDg5MzkpKSAlPiUKICAgbGVmdF9qb2luKAogICAgIGRhdCAlPiUKICAgICAgIHN0X3NldF9nZW9tZXRyeShOVUxMKSAlPiUKICAgICAgIGdyb3VwX2J5KE5BTUUpICU+JQogICAgICAgc3VtbWFyaXplKGNvdW50eV9wb3B1bGF0aW9uXzIwMjAgPSByb3VuZChzdW0odG90YWxfcG9wdWxhdGlvbjIwMjApKSkpCgpjb3VudHlQb3B1bGF0aW9uXzIwNDAgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLFZhbHVlLCAtTkFNRSkgJT4lCiAgZ2dwbG90KGFlcyhyZW9yZGVyKE5BTUUsLVZhbHVlKSxWYWx1ZSkpICsKICBnZW9tX2JhcihhZXMoZmlsbD1WYXJpYWJsZSksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMjA0MCIsIjIwMjAiKSwKICAgICAgICAgICAgICAgICAgICBuYW1lPSJQb3B1bGF0aW9uIikgKwogIGxhYnModGl0bGU9IlBvcHVsYXRpb24gQ2hhbmdlIGJ5IENvdW50eTogMjAyMCAtIDIwNDAiLAogICAgICAgeD0iQ291bnR5IiwgeT0iUG9wdWxhdGlvbiIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgcGxvdFRoZW1lCmBgYAoKIyMgNS4yLiBQcmVkaWN0aW5nIERldmVsb3BtZW50IERlbWFuZAoKTmV4dCwgdGhlIGBjb3VudHlQb3B1bGF0aW9uXzIwNDBgIHRhYmxlIGlzIGpvaW5lZCB0byBgZGF0YCBhbmQgYHBvcF9jaGFuZ2VgIGluIG9yZGVyIHRvIOKAmGRpc3RyaWJ1dGXigJkgdGhlIG5ldyBwb3B1bGF0aW9uIGFjcm9zcyB0aGUgc3R1ZHkgYXJlYS4gVG8gZG8gc28sIHRoZSB0aGUgYWxsb2NhdGlvbiBvZiBuZXcgcG9wdWxhdGlvbiBpcyB3ZWlnaHRlZCBieSBhIGdyaWQgY2VsbOKAmXMgZXhpc3RpbmcgcG9wdWxhdGlvbiAoYHBvcF8yMDQwLmluZmlsbGApLiAyMDIwIHBvcHVsYXRpb24gaXMgc3VidHJhY3RlZCBmcm9tIHRoaXMgZmlndXJlIHRvIGdldCBgcG9wX0NoYW5nZWAuIEZpbmFsbHksIGBNb2RlbDZgIGlzIHVzZWQgdG8gcHJlZGljdCBmb3IgMjA0MCBnaXZlbiB0aGUgdXBkYXRlZCBwb3B1bGF0aW9uIGNoYW5nZSBhbmQgbGFnIGRldmVsb3BtZW50IGZlYXR1cmVzLgoKVGhlIG1hcCBvZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyB0aGF0IHJlc3VsdHMgaXMgYmVzdCB0aG91Z2h0IG9mIGFzIGEgbWVhc3VyZSBvZiBwcmVkaWN0ZWQgZGV2ZWxvcG1lbnQgZGVtYW5kIGluIDIwNDAuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdF9pbmZpbGwgPC0KICBkYXQgJT4lCiAgI2NhbGN1bGF0ZSBwb3B1bGF0aW9uIGNoYW5nZQogICAgbGVmdF9qb2luKGNvdW50eVBvcHVsYXRpb25fMjA0MCkgJT4lCiAgICBtdXRhdGUocHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wID0gdG90YWxfcG9wdWxhdGlvbjIwMjAgLyBjb3VudHlfcG9wdWxhdGlvbl8yMDIwLAogICAgICAgICAgIHBvcF8yMDQwLmluZmlsbCA9IHByb3BvcnRpb25fb2ZfY291bnR5X3BvcCAqIGNvdW50eV9wcm9qZWN0aW9uXzIwNDAsCiAgICAgICAgICAgcG9wX0NoYW5nZSA9IHJvdW5kKHBvcF8yMDQwLmluZmlsbCAtIHRvdGFsX3BvcHVsYXRpb24yMDIwKSwyKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWNvdW50eV9wcm9qZWN0aW9uXzIwNDAsIC1jb3VudHlfcG9wdWxhdGlvbl8yMDIwLCAKICAgICAgICAgICAgICAgICAgLXByb3BvcnRpb25fb2ZfY291bnR5X3BvcCwgLXBvcF8yMDQwLmluZmlsbCkgJT4lCiAgI2FkZCB2YWx1ZXMgZm9yIDIwMjAgYmFzZWxpbmUgKGZvcmVzdCwgZmFybSwgZXRjLikKICAKICAjcHJlZGljdCBmb3IgMjA0MAogICAgbXV0YXRlKHByZWRpY3RfMjA0MC5pbmZpbGwgPSBwcmVkaWN0KE1vZGVsMywuICwgdHlwZT0icmVzcG9uc2UiKSkKCiMgZGF0X2luZmlsbCA8LSBkYXRfaW5maWxsICU+JSAKIyAgIG11dGF0ZShwcmVkaWN0XzIwNDAuaW5maWxsMiA9aWZlbHNlKHByZWRpY3RfMjA0MC5pbmZpbGwgPCAwLjAxIHwgcHJlZGljdF8yMDQwLmluZmlsbCA+IDEsIDAsIHByZWRpY3RfMjA0MC5pbmZpbGwpKQoKZGF0X2luZmlsbCAlPiUKICBnZ3Bsb3QoKSArICAKICBnZW9tX3NmKGFlcyhmaWxsID0gcHJlZGljdF8yMDQwLmluZmlsbCksIGNvbG9yID0gInRyYW5zcGFyZW50IikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gcGFsZXR0ZTVbMV0sCiAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gcGFsZXR0ZTVbbGVuZ3RoKHBhbGV0dGU1KV0sCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlN0cmV0Y2hlZFxuVmFsdWVzIikgKwogIGdlb21fc2YoZGF0YSA9IHN0dWR5QXJlYUNvdW50aWVzLCBmaWxsID0gTkEsIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJEZXZlbG9wbWVudCBEZW1hbmQgaW4gMjA0MDogUHJlZGljdGVkIFByb2JhYmlsaXRpZXMiKSArCiAgbWFwVGhlbWUKCmBgYAoKIyA2LiBDb21wYXJpbmcgUHJlZGljdGVkIERldmVsb3BtZW50IERlbWFuZCAmIEVudmlyb25tZW50YWwgU2Vuc2l0aXZpdHkKCldlIG5vdyBoYXZlIGEgcmVhbGx5IHN0cm9uZyBpbmRpY2F0b3Igb2YgZGV2ZWxvcG1lbnQgZGVtYW5kIGZvciAyMDQwIHRvIGhlbHAgZ3VpZGUgbG9jYWwgbGFuZCB1c2UgcGxhbm5pbmcuIERlbWFuZCBob3dldmVyLCBpcyBvbmx5IG9uZSBzaWRlIG9mIHRoZSBlcXVhdGlvbi4gSXQgbXVzdCBiYWxhbmNlZCB3aXRoIHRoZSBzdXBwbHkgb2YgZW52aXJvbm1lbnRhbGx5IHNlbnNpdGl2ZSBsYW5kLiBVbmRlcnN0YW5kaW5nIHRoZSBpbnRlcnBsYXkgYmV0d2VlbiBkZW1hbmQgYW5kIHN1cHBseSBpcyB0aGUgZmlyc3Qgc3RhZ2Ugb2YgdGhlIOKAmEFsbG9jYXRpb27igJkgcGhhc2UsIHdoZXJlIFBsYW5uZXJzIHVsdGltYXRlbHkgZGVjaWRlIHdoaWNoIGxhbmQgc2hvdWxkIGJlIGRldmVsb3BlZCBhbmQgd2hpY2ggc2hvdWxkIG5vdC4KCkZvciB0aGlzIGFuYWx5c2lzIGZhcm1sYW5kIGFuZCB1bmRldmVsb3BlZCBsYW5kIGFyZSBiZSBkZWVtZWQgYFN1aXRhYmxlYCwgd2hpbGUgZW52aXJvbm1lbnRhbGx5IHNlbnNpdGl2ZSBhcmVhcyBsaWtlIHdldGxhbmRzIGFuZCBmb3Jlc3QgYXJlIGJlIGRlZW1lZCBgTm90IFN1aXRhYmxlYC4gQmVsb3csIDIwMTkgbGFuZCBjb3ZlciBkYXRhIGlzIHJlYWQgaW4gYW5kIHNldmVyYWwgbWVhc3VyZXMgb2YgZW52aXJvbm1lbnRhbCBzZW5zaXRpdml0eSBhcmUgY3JlYXRlZCBieSBjb3VudHkuIFRoZXNlIGluY2x1ZGU6CgoxLiBUaGUgdG90YWwgYW1vdW50IG9mIHdldGxhbmRzIGFuZCBmb3Jlc3QgbGFuZCBjb3ZlciBhcmVhIGluIDIwMTkuCjIuIFRoZSBhbW91bnQgb2Ygc2Vuc2l0aXZlIGxhbmQgKHdldGxhbmQgYW5kIGZvcmVzdCkgbG9zdCBiZXR3ZWVuIDIwMDEgYW5kIDIwMTkuCjMuIFRoZSB0b3RhbCBhcmVhIG9mIGxhcmdlIHNlbnNpdGl2ZSBsYW5kc2NhcGUg4oCYcGF0Y2hlc+KAmSBpbiAyMDE5LgoKVGhlIHRoaXJkIG1ldHJpYyB3YXJyYW50cyBzb21lIGZ1cnRoZXIgZGlzY3Vzc2lvbi4gTGVhcGZyb2cgZGV2ZWxvcG1lbnQgaXMgdGhlIGlkZWEgdGhhdCBkaXNjb250aW51b3VzIGRldmVsb3BtZW50IGFjcm9zcyBzcGFjZSBjYXJ2ZXMgb3V0IGRpc2pvaW50ZWQgcGF0Y2hlcyBvZiBoYWJpdGF0LiBUaGlzIGZyYWdtZW50YXRpb24gcmVkdWNlcyBiaW9kaXZlcnNpdHkgcGFydGljdWxhcmx5IGZvciBzcGVjaWVzIHRoYXQgbmVlZCByb29tIHRvIHJvYW0uIEJlbG93LCBlbnZpcm9ubWVudGFsbHkgYHNlbnNpdGl2ZV9yZWdpb25zYCBhcmUgY3JlYXRlZCB0byByZXByZXNlbnQgbGFyZ2UgYXJlYXMgb2YgdW4tZnJhZ21lbnRlZCBoYWJpdGF0LiBXZSB0aGVuIGNhbGN1bGF0ZSB0aGUgdG90YWwgYXJlYSBvZiB0aGVzZSBjbHVtcHMgZm9yIGVhY2ggY291bnR5Lgo8YnI+CiMjIDYuMS4gMjAxOSBMYW5kIENvdmVyIERhdGEgIAoKVG8gYmVnaW4sIHRoZSAyMDE5IExhbmQgQ292ZXIgZGF0YSBpcyByZWFkIGluIGFuZCByZWNsYXNzaWZpZWQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgUmVhZCByYXN0ZXIgZGF0YQpsY18yMDE5IDwtIHJhc3RlcigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2NjMTkxZTdlYzgxZTg2MzY3MjA0MGY3NDc0NWZiZjliODAxNWI2OTMvZGF0YS9DdmlsbGVfTENfMjAxOS50aWYiKQoKZ2dwbG90KCkgKwogIGdlb21fcmFzdGVyKGRhdGEgPSByYmluZChyYXN0KGxjXzIwMDEpICU+JSBtdXRhdGUobGFiZWwgPSAiMjAwMSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICByYXN0KGxjXzIwMTkpICU+JSBtdXRhdGUobGFiZWwgPSAiMjAxOSIpKSAlPiUgCiAgICAgICAgICAgICAgbmEub21pdCAlPiUgZmlsdGVyKHZhbHVlID4gMCksIAogICAgICAgICAgICAgIGFlcyh4LHksZmlsbD1hcy5mYWN0b3IodmFsdWUpKSkgKwogIGdlb21fc2YoZGF0YSA9IHN0dWR5QXJlYUNvdW50aWVzLCBmaWxsID0gTkEsIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMSkgKwogIGZhY2V0X3dyYXAofmxhYmVsKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUsIG5hbWUgPSIiKSArCiAgbGFicyh0aXRsZSA9ICJMYW5kIENvdmVyLCAyMDAxICYgMjAxOSIpICsKICBtYXBUaGVtZSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpOZXh0LCBlYWNoIHJhc3RlciBpcyBhZ2dyZWdhdGVkIHRvIHRoZSBmaXNobmV0IHVzaW5nIHRoZSBgYWdncmVnYXRlUmFzdGVyYCBmdW5jdGlvbiBhbmQgMjAxOSBsYW5kIGNvdmVyIHR5cGVzIGFyZSBtYXBwZWQuCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRldmVsb3BlZDE5IDwtIGxjXzIwMTkgPT0gMjEgfCBsY18yMDE5ID09IDIyIHwgbGNfMjAxOSA9PSAyMyB8IGxjXzIwMTkgPT0gMjQKZm9yZXN0MTkgPC0gbGNfMjAxOSA9PSA0MSB8IGxjXzIwMTkgPT0gNDIgfCBsY18yMDE5ID09IDQzCmZhcm0xOSA8LSBsY18yMDE5ID09IDgxIHwgbGNfMjAxOSA9PSA4Mgp3ZXRsYW5kczE5IDwtIGxjXzIwMTkgPT0gOTAgfCBsY18yMDE5ID09IDk1Cm90aGVyVW5kZXZlbG9wZWQxOSA8LSBsY18yMDE5ID09IDUyIHwgbGNfMjAxOSA9PSA3MSB8IGxjXzIwMTkgPT0gMzEKd2F0ZXIxOSA8LSBsY18yMDE5ID09IDExCgpuYW1lcyhkZXZlbG9wZWQxOSkgPC0gImRldmVsb3BlZDE5IgpuYW1lcyhmb3Jlc3QxOSkgPC0gImZvcmVzdDE5IgpuYW1lcyhmYXJtMTkpIDwtICJmYXJtMTkiCm5hbWVzKHdldGxhbmRzMTkpIDwtICJ3ZXRsYW5kczE5IgpuYW1lcyhvdGhlclVuZGV2ZWxvcGVkMTkpIDwtICJvdGhlclVuZGV2ZWxvcGVkMTkiCm5hbWVzKHdhdGVyMTkpIDwtICJ3YXRlcjE5IgoKdGhlUmFzdGVyTGlzdDE5IDwtIGMoZGV2ZWxvcGVkMTksZm9yZXN0MTksZmFybTE5LHdldGxhbmRzMTksb3RoZXJVbmRldmVsb3BlZDE5LHdhdGVyMTkpCgojIyNhc3NpZ24gbGMgdmFsdWVzIGJhc2VkIG9uIGhpZXJhcmNoeQpkYXQyIDwtCiAgYWdncmVnYXRlUmFzdGVyKHRoZVJhc3Rlckxpc3QxOSwgZGF0X2luZmlsbCkgJT4lCiAgZHBseXI6OnNlbGVjdChkZXZlbG9wZWQxOSxmb3Jlc3QxOSxmYXJtMTksd2V0bGFuZHMxOSxvdGhlclVuZGV2ZWxvcGVkMTksd2F0ZXIxOSkgJT4lCiAgc3Rfc2V0X2dlb21ldHJ5KE5VTEwpICU+JQogIGJpbmRfY29scyguLGRhdCkgJT4lCiAgc3Rfc2YoKSAlPiUKICBzdF9jYXN0KCJQT0xZR09OIikKIyByZWFzc2lnbiBjZWxscyB3aXRoIG1vcmUgdGhhbiBvbmUgbGMgdmFsdWUgdG8ganVzdCBvbmUgYmFzZWQgb24gaGllcmFyY2h5IHNob3duIGhlcmUKZGF0MiA8LSBkYXQyICU+JQogICAgbXV0YXRlKGZhcm0xOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDEsIDEsIDApKSAlPiUKICAgIG11dGF0ZShmb3Jlc3QxOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDAgJiBmb3Jlc3QxOSA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUod2V0bGFuZHMxOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDAgJiBmb3Jlc3QxOSA9PSAwICYgd2V0bGFuZHMxOSA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUod2F0ZXIxOSA9IGlmZWxzZShkZXZlbG9wZWQxOSA9PSAwICYgZmFybTE5ID09IDAgJiBmb3Jlc3QxOSA9PSAwICYgd2V0bGFuZHMxOSA9PSAwICYgd2F0ZXIxOSA9PSAxLCAxLCAwKSkgJT4lCiAgICBtdXRhdGUob3RoZXJVbmRldmVsb3BlZDE5ID0gaWZlbHNlKGRldmVsb3BlZDE5ID09IDAgJiBmYXJtMTkgPT0gMCAmIGZvcmVzdDE5ID09IDAgJiB3ZXRsYW5kczE5ID09IDAgJiB3YXRlcjE5ID09IDAgJiBvdGhlclVuZGV2ZWxvcGVkMTkgPT0gMSwgMSwgMCkpCgoKZGF0MiAlPiUKICBnYXRoZXIodmFyLHZhbHVlLGRldmVsb3BlZDE5OndhdGVyMTkpICU+JQogIHN0X2NlbnRyb2lkKCkgJT4lCiAgbXV0YXRlKFggPSBzdF9jb29yZGluYXRlcyguKVssMV0sCiAgICAgICAgIFkgPSBzdF9jb29yZGluYXRlcyguKVssMl0pICU+JQogIGdncGxvdCgpICsKICAgIGdlb21fc2YoZGF0YT1DdmlsbGVNU0EpICsKICAgIGdlb21fcG9pbnQoYWVzKFgsWSwgY29sb3VyPWFzLmZhY3Rvcih2YWx1ZSkpLCBzaXplID0gMC4xKSArCiAgICBmYWNldF93cmFwKH52YXIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIkxhbmQgQ292ZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArCiAgICBsYWJzKHRpdGxlID0gIkxhbmQgQ292ZXIgVHlwZXMsIDIwMTkiLAogICAgICAgICBzdWJ0aXRsZSA9ICJBcyBmaXNobmV0IGNlbnRyb2lkcyIpICsKICAgbWFwVGhlbWUKCmBgYAoKTmV4dCwgd2UgcmVmb3JtYXQgMjAxOSBMYW5kIENvdmVyIGZvciBvdXIgTW9kZWwsIHByZXNlcnZpbmcgMjAwMSBmZWF0dXJlcyBhcyBuZXcgY29sdW1ucy4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZGF0MiA8LSBkYXQyICU+JQojIDIwMDEgbGFuZCBjb3ZlcgogICAgbXV0YXRlKGZhcm0wMSA9IGZhcm0pICU+JQogICAgbXV0YXRlKGZvcmVzdDAxID0gZm9yZXN0KSAlPiUKICAgIG11dGF0ZSh3ZXRsYW5kczAxID0gd2V0bGFuZHMpICU+JQogICAgbXV0YXRlKHdhdGVyMDEgPSB3YXRlcikgJT4lCiAgICBtdXRhdGUob3RoZXJVbmRldmVsb3BlZDAxID0gb3RoZXJVbmRldmVsb3BlZCkgJT4lCiAgICBtdXRhdGUodG90YWxfaG91c2luZ191bml0czAxID0gdG90YWxfaG91c2luZ191bml0cykgJT4lCiAgICBtdXRhdGUodG90YWxfd2hpdGUwMSA9IHRvdGFsX3doaXRlKSAlPiUgCiMgMjAxOSBsYW5kIGNvdmVyCiAgICBtdXRhdGUoZmFybSA9IGZhcm0xOSkgJT4lCiAgICBtdXRhdGUoZm9yZXN0ID0gZm9yZXN0MTkpICU+JQogICAgbXV0YXRlKHdldGxhbmRzID0gd2V0bGFuZHMxOSkgJT4lCiAgICBtdXRhdGUod2F0ZXIgPSB3YXRlcjE5KSAlPiUKICAgIG11dGF0ZShvdGhlclVuZGV2ZWxvcGVkID0gb3RoZXJVbmRldmVsb3BlZDE5KSAlPiUKICAgIG11dGF0ZSh0b3RhbF9ob3VzaW5nX3VuaXRzID0gdG90YWxfaG91c2luZ191bml0czIwMjApICU+JQogICAgbXV0YXRlKHRvdGFsX3doaXRlID0gdG90YWxfd2hpdGUyMDIwKQpgYGAKCiMjIDYuMi4gU2Vuc2l0aXZlIExhbmQgQ292ZXIgTG9zdAoKQmVsb3cgYW4gaW5kaWNhdG9yIGBzZW5zaXRpdmVfbG9zdGAgaXMgY3JlYXRlZCBpbmRpY2F0aW5nIGdyaWQgY2VsbHMgdGhhdCB3ZXJlIGVpdGhlciBmb3Jlc3Qgb3Igd2V0bGFuZHMgaW4gMjAwMSBidXQgd2VyZSBubyBsb25nZXIgc28gaW4gMjAxOS4gVGhlIG91dHB1dCBsYXllciwgYHNlbnNpdGl2ZV9sYW5kX2xvc3RgLCBnaXZlcyBhIHNlbnNlIGZvciBob3cgZGV2ZWxvcG1lbnQgaW4gdGhlIHJlY2VudCBwYXN0IGhhcyBlZmZlY3RlZCB0aGUgbmF0dXJhbCBlbnZpcm9ubWVudC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gNn0KZGF0MiA8LQogIGRhdDIgJT4lCiAgIG11dGF0ZShzZW5zaXRpdmVfbG9zdDE5ID0gaWZlbHNlKGZvcmVzdDAxID09IDEgJiBmb3Jlc3QxOSA9PSAwIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2V0bGFuZHMwMSA9PSAxICYgd2V0bGFuZHMxOSA9PSAwLDEsMCkpCiAgICAgICAgICAgICAgICAgICAgICAKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kYXQyLCBhZXMoZmlsbCA9YXMuZmFjdG9yKHNlbnNpdGl2ZV9sb3N0MTkpKSwgY29sb3IgPSAidHJhbnNwYXJlbnQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiTm8gQ2hhbmdlIiwiU2Vuc2l0aXZlIExvc3QiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiIikgKwogIGxhYnModGl0bGUgPSAiU2Vuc2l0aXZlIGxhbmRzIGxvc3Q6IDIwMDEgLSAyMDE5IikgKwogIG1hcFRoZW1lCmBgYAoKIyMgNi4zIExhbmRzY2FwZSBGcmFnbWVudGF0aW9uCgpJbiB0aGlzIHNlY3Rpb24sIHRoZSBgd2V0bGFuZHMxOWAgYW5kIGBmb3Jlc3QxOWAgcmFzdGVycyBhcmUgY29udmVydGVkIHRvIGNvbnRpZ3VvdXMgYHNlbnNpdGl2ZV9yZWdpb25zYCB1c2luZyB0aGUgYHJhc3Rlcjo6Y2x1bXBgIGZ1bmN0aW9uLiBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gUmVnaW9uIEdyb3VwIGluIEFyY0dJUy4gVGhlIHJhc3RlciBjbHVtcHMgYXJlIHRoZW4gY29udmVydGVkIHRvIHZlY3RvciBgc2ZgIGxheWVyczsgZGlzc29sdmVkIGludG8gdW5pcXVlIHJlZ2lvbnM7IEFjcmVzIGFyZSBjYWxjdWxhdGVkOyBhbmQgdGhlIGxheWVycyBhcmUgY29udmVydGVkIGJhY2sgdG8gcmFzdGVyIHRvIGJlIGV4dHJhY3RlZCBiYWNrIHRvIHRoZSBmaXNobmV0IHdpdGggYGFnZ3JlZ2F0ZVJhc3RlcmAuIE5vdGUgdGhhdCBvbmx5IGBzZW5zaXRpdmVfcmVnaW9uc2Agd2l0aCBhcmVhcyBncmVhdGVyIHRoYW4gMSBhY3JlIGFyZSBpbmNsdWRlZC4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aD0gNn0Kc2Vuc2l0aXZlUmVnaW9ucyA8LSAKICByYXN0ZXI6OmNsdW1wKHdldGxhbmRzMTkgKyBmb3Jlc3QxOSkgJT4lCiAgcmFzdGVyVG9Qb2x5Z29ucygpICU+JQogIHN0X2FzX3NmKCkgJT4lCiAgZ3JvdXBfYnkoY2x1bXBzKSAlPiUgCiAgc3VtbWFyaXplKCkgJT4lCiAgICBtdXRhdGUoQWNyZXMgPSBhcy5udW1lcmljKHN0X2FyZWEoLikgKiAwLjAwMDAyMjk1NjgpKSAlPiUKICAgIGZpbHRlcihBY3JlcyA+IDM5NTQpICAlPiUKICBkcGx5cjo6c2VsZWN0KCkgJT4lCiAgcmFzdGVyOjpyYXN0ZXJpemUoLixlbXB0eVJhc3RlcikgCnNlbnNpdGl2ZVJlZ2lvbnNbc2Vuc2l0aXZlUmVnaW9ucyA+IDBdIDwtIDEgIApuYW1lcyhzZW5zaXRpdmVSZWdpb25zKSA8LSAic2Vuc2l0aXZlUmVnaW9ucyIKCmRhdDIgPC0KICBhZ2dyZWdhdGVSYXN0ZXIoYyhzZW5zaXRpdmVSZWdpb25zKSwgZGF0MikgJT4lCiAgZHBseXI6OnNlbGVjdChzZW5zaXRpdmVSZWdpb25zKSAlPiUKICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgYmluZF9jb2xzKC4sZGF0MikgJT4lCiAgc3Rfc2YoKQoKZ2dwbG90KCkgKwogICAgZ2VvbV9zZihkYXRhPWRhdDIsIGFlcyhmaWxsID1hcy5mYWN0b3Ioc2Vuc2l0aXZlUmVnaW9ucykpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJPdGhlciIsIlNlbnNpdGl2ZSBSZWdpb25zIiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lPSIiKSArCiAgbGFicyh0aXRsZSA9ICJTZW5zaXRpdmUgcmVnaW9ucyIsCiAgICAgICBzdWJ0aXRsZSA9ICJDb250aW5vdXMgYXJlYXMgb2YgZWl0aGVyIHdldGxhbmRzIG9yIGZvcmVzdHNcbmdyZWF0ZXIgdGhhbiAxIGFjcmUiKSArCiAgbWFwVGhlbWUKYGBgCgojIyA2LjQuIFN1bW1hcml6ZSBieSBDb3VudHkKClRoZSBiZWxvdyBgZHBseXJgIHN0YXRlbWVudCB0YWtlcyBhcyBpdHMgaW5wdXQsIGBkYXQyYCwgd2hpY2ggd2FzIGNyZWF0ZWQgaW4gU2VjdGlvbnMgNi4yIC0gNi40IGFuZCB3cmFuZ2xlcyB0b2dldGhlciBhIHRhYmxlIG9mIGNvdW50eS1sZXZlbCwgc3VwcGx5IGFuZCBkZW1hbmQgbWV0cmljcyB3aGljaCBjYW4gYmUgdXNlZCB0byBhbmFseXplIHN1aXRhYmlsaXR5IGJ5IGNvdW50eS4KYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpjb3VudHlfc3BlY2lmaWNfbWV0cmljcyA8LSAKICBkYXQyICU+JQogICNwcmVkaWN0IGRldmVsb3BtZW50IGRlbWFuZCBmcm9tIG91ciBtb2RlbAogIG11dGF0ZShEZXZlbG9wbWVudF9EZW1hbmQgPSBwcmVkaWN0KE1vZGVsNiwgZGF0MiwgdHlwZT0icmVzcG9uc2UiKSkgJT4lCiAgI2dldCBhIGNvdW50IGNvdW50IG9mIGdyaWQgY2VsbHMgYnkgY291bnR5IHdoaWNoIHdlIGNhbiB1c2UgdG8gY2FsY3VsYXRlIHJhdGVzIGJlbG93CiAgbGVmdF9qb2luKHN0X3NldF9nZW9tZXRyeShkYXQsIE5VTEwpICU+JSBncm91cF9ieShOQU1FKSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKSkgJT4lCiAgI2NhbGN1bGF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgYnkgY291bnR5CiAgZ3JvdXBfYnkoTkFNRSkgJT4lCiAgc3VtbWFyaXplKFRvdGFsX0Zhcm1sYW5kID0gc3VtKGZhcm0xOSkgLyBtYXgoY291bnQpLAogICAgICAgICAgICBUb3RhbF9Gb3Jlc3QgPSBzdW0oZm9yZXN0MTkpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgVG90YWxfV2V0bGFuZHMgPSBzdW0od2V0bGFuZHMxOSkgLyBtYXgoY291bnQpLAogICAgICAgICAgICBUb3RhbF9VbmRldmVsb3BlZCA9IHN1bShvdGhlclVuZGV2ZWxvcGVkMTkpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgU2Vuc2l0aXZlX0xhbmRfTG9zdCA9IHN1bShzZW5zaXRpdmVfbG9zdDE5KSAvIG1heChjb3VudCksCiAgICAgICAgICAgIFNlbnNpdGl2ZV9SZWdpb25zID0gc3VtKHNlbnNpdGl2ZVJlZ2lvbnMpIC8gbWF4KGNvdW50KSwKICAgICAgICAgICAgTWVhbl9EZXZlbG9wbWVudF9EZW1hbmQgPSBtZWFuKERldmVsb3BtZW50X0RlbWFuZCkpICU+JQogICNnZXQgcG9wdWxhdGlvbiBkYXRhIGJ5IGNvdW50eQogIGxlZnRfam9pbihjb3VudHlQb3B1bGF0aW9uXzIwNDAgJT4lIAogICAgICAgICAgICBtdXRhdGUoUG9wdWxhdGlvbl9DaGFuZ2UgPSBjb3VudHlfcHJvamVjdGlvbl8yMDQwIC0gY291bnR5X3BvcHVsYXRpb25fMjAyMCwKICAgICAgICAgICAgICAgICAgIFBvcHVsYXRpb25fQ2hhbmdlX1JhdGUgPSBQb3B1bGF0aW9uX0NoYW5nZSAvIGNvdW50eV9wcm9qZWN0aW9uXzIwNDApICU+JQogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KE5BTUUsUG9wdWxhdGlvbl9DaGFuZ2VfUmF0ZSkpCmBgYAoKCk5vdyBhIHNtYWxsIG11bHRpcGxlIHBsb3QgY2FuIGJlIGNyZWF0ZWQgcHJvdmlkaW5nIGJvdGggc3VwcGx5IGFuZCBkZW1hbmQgc2lkZSBhbmFseXRpY3MgYnkgY291bnR5LiBUaGUgcGxvdCBnaXZlcyBhIHNlbnNlIGZvciBkZXZlbG9wbWVudCBkZW1hbmQgKGBEZW1hbmQtU2lkZWApLCBzdWl0YWJsZSBsYW5kIGZvciBkZXZlbG9wbWVudCAoYFN1aXRhYmxlYCkgYW5kIHNlbnNpdGl2ZSBsYW5kIChgTm90IFN1aXRhYmxlYCkuCgpUaGUgZGF0YSBzdWdnZXN0cyBib3RoIHBvcHVsYXRpb24gYW5kIGRldmVsb3BtZW50IGRlbWFuZCB3aWxsIGluY3JlYXNlIGZvciBDaGFybG90dGVzdmlsbGUgTVNBLiBIb3dldmVyLCBjb21wYXJlZCB0byBDaGFybG90dGVzdmlsbGUgY291bnR5LCBBbGJlbWFybGUgaGFzIGEgaGlnaCByYXRlIG9mIGRldmVsb3BhYmxlIGZhcm1sYW5kIGFuZCBhIGxvdyBzdXBwbHkgb2Ygc2Vuc2l0aXZlIGxhbmQuIFRoaXMgc3VnZ2VzdHMgQWxiZW1hcmxlIGlzIG1vcmUgc3VpdGFibGUgdG8gbmV3IGRldmVsb3BtZW50IHRoYW4gQ2hhcmxvdHRlc3ZpbGxlLCB3aGljaCBpcyBoaWdobHkgZGV2ZWxvcGVkLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpjb3VudHlfc3BlY2lmaWNfbWV0cmljcyAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtTkFNRSwgLWdlb21ldHJ5KSAlPiUKICBtdXRhdGUoVmFyaWFibGUgPSBmYWN0b3IoVmFyaWFibGUsIGxldmVscz1jKCJQb3B1bGF0aW9uX0NoYW5nZV9SYXRlIiwiTWVhbl9EZXZlbG9wbWVudF9EZW1hbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRvdGFsX0Zhcm1sYW5kIiwiVG90YWxfVW5kZXZlbG9wZWQiLCJUb3RhbF9Gb3Jlc3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRvdGFsX1dldGxhbmRzIiwiU2Vuc2l0aXZlX0xhbmRfTG9zdCIsIlNlbnNpdGl2ZV9SZWdpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFKSkpICU+JQogIG11dGF0ZShQbGFubmluZ19EZXNpZ25hdGlvbiA9IGNhc2Vfd2hlbigKICAgIFZhcmlhYmxlID09ICJQb3B1bGF0aW9uX0NoYW5nZV9SYXRlIiB8IFZhcmlhYmxlID09ICJNZWFuX0RldmVsb3BtZW50X0RlbWFuZCIgfiAiRGVtYW5kLVNpZGUiLAogICAgVmFyaWFibGUgPT0gIlRvdGFsX0Zhcm1sYW5kIiB8IFZhcmlhYmxlID09ICJUb3RhbF9VbmRldmVsb3BlZCIgICAgICAgICAgICAgICB+ICJTdWl0YWJsZSIsCiAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gIk5vdCBTdWl0YWJsZSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9VmFyaWFibGUsIHk9VmFsdWUsIGZpbGw9UGxhbm5pbmdfRGVzaWduYXRpb24pKSArCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCksIGNvbG91cj0iYmxhY2siKSArCiAgICBmYWNldF93cmFwKH5OQU1FLCBuY29sPTUpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC4yNSwgMSwgYnkgPSAuMjUpKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAyLjUpICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNC41KSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJyZWQiLCJkYXJrZ3JlZW4iKSkgKwogICAgbGFicyh0aXRsZT0gIkNvdW50eSBTcGVjaWZpYyBBbGxvY2F0aW9uIE1ldHJpY3MiLCBzdWJ0aXRsZT0gIkFzIHJhdGVzIiwgeD0iSW5kaWNhdG9yIiwgeT0iUmF0ZSIpICsKICAgIHBsb3RUaGVtZSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksIGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKYGBgCgojIDcuIEFsbG9jYXRpb24KCkFsbG9jYXRpb24gaXMgdGhlIGZpbmFsIHN0YWdlIG9mIHRoZSB1cmJhbiBncm93dGggbW9kZWxpbmcgcHJvY2Vzcy4gTm93IHRoYXQgYm90aCBkZW1hbmQgYW5kIHN1cHBseSBpcyB1bmRlcnN0b29kLCBXZSBjYW4gYWxsb2NhdGUgZGV2ZWxvcG1lbnQgcmlnaHRzIGFjY29yZGluZ2x5LiBPZiBjb3Vyc2UsIHRoaXMgY291bGQgdGFrZSBtYW55IGZvcm1zIG9mIHJlZ3VsYXRpb24gaW5jbHVkaW5nIHpvbmluZywgc3ViZGl2aXNpb24gYXBwcm92YWwgb3Igb3V0cmlnaHQgY29uc2VydmF0aW9uLiBJbiB0aGlzIHNlY3Rpb24sIGRlbWFuZCBhbmQgc3VwcGx5IGFyZSB2aXN1YWxpemVkIGZvciB0d28gY291bnRpZXMsIENoYXJsb3R0ZXN2aWxsZSBhbmQgQWxiZW1hcmxlLiBUaGUgZGF0YSBzdWdnZXN0cyB0aGF0IHRoZSBsYXR0ZXIgaXMgbW9yZSBjb25kdWNpdmUgdG8gZ3Jvd3RoIHdoaWxlIHRoZSBmb3JtZXIgaXMgbGVzcyBzby4KCkZpcnN0LCBkZXZlbG9wbWVudCBkZW1hbmQgaXMgcHJlZGljdGVkIGZvciBBbGJlbWFybGUuIFRoZW4gYSBsYXllciwgYEFsYmVtYXJsZV9sYW5kVXNlYCBpcyBjcmVhdGVkLCB0aGF0IGluY2x1ZGVzIGluZGljYXRvcnMgZm9yIGJvdGggcHJldmlvdXNseSBkZXZlbG9wZWQgbGFuZCBhbmQgZW52aXJvbm1lbnRhbGx5IHVuc3VpdGFibGUgbGFuZC4gVGhpcyBsYXllciB0aGVuIGlzIG92ZXJsYXllZCBhdG9wIGRldmVsb3BtZW50IGRlbWFuZCBhbmQgcHJvamVjdGVkIHBvcHVsYXRpb24gY2hhbmdlIHRvIGdpdmUgdGhlIGZ1bGwgc3VwcGx5IGFuZCBkZW1hbmQtc2lkZSBwaWN0dXJlIGluIEFsYmVtYXJsZS4KClRoZXJlIGFyZSBzb21lIGNsZWFyIG9wcG9ydHVuaXRpZXMgZm9yIGRldmVsb3BtZW50IGluIEFsYmVtYXJsZS4gU2lnbmlmaWNhbnQgaW5maWxsIG9wcG9ydHVuaXRpZXMgZXhpc3QgYWxvbmcgdGhlIHJvYWRzIGFuZCBoaWdod2F5cyB3aGVyZSBwb3B1bGF0aW9uIGNoYW5nZSBpcyBwcm9qZWN0ZWQgdG8gYmUgZ3JlYXRlc3QuIFRoZXJlIGlzIGFsc28gYSBnb29kIGRlYWwgb2YgZW52aXJvbm1lbnRhbGx5IHN1aXRhYmxlIGxhbmQgYWxvbmcgdGhlIGhpZ2h3YXlzLiBUaGlzIHdvdWxkIGJlIGlkZWFsIHNwYWNlIGZvciBsYW5kIGRldmVsb3BtZW50cy4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSAxMX0KQWxiZW1hcmxlIDwtCiAgZGF0MiAlPiUgICAjY2FsY3VsYXRlIHBvcHVsYXRpb24gY2hhbmdlCiAgICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDQwKSAlPiUKICAgIG11dGF0ZShwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgPSB0b3RhbF9wb3B1bGF0aW9uMjAyMCAvIGNvdW50eV9wb3B1bGF0aW9uXzIwMjAsCiAgICAgICAgICAgcG9wXzIwNDAuaW5maWxsID0gcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wICogY291bnR5X3Byb2plY3Rpb25fMjA0MCwKICAgICAgICAgICBwb3BfQ2hhbmdlID0gKHBvcF8yMDQwLmluZmlsbCAtIHRvdGFsX3BvcHVsYXRpb24yMDIwKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1jb3VudHlfcHJvamVjdGlvbl8yMDQwLCAtY291bnR5X3BvcHVsYXRpb25fMjAyMCwgCiAgICAgICAgICAgICAgICAgIC1wcm9wb3J0aW9uX29mX2NvdW50eV9wb3AsIC1wb3BfMjA0MC5pbmZpbGwpICU+JQogICAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlPSJyZXNwb25zZSIpKSAlPiUKICAgIGZpbHRlcihOQU1FID09ICJBbGJlbWFybGUiKSAKCgpBbGJlbWFybGVfbGFuZFVzZSA8LSByYmluZCgKICBmaWx0ZXIoQWxiZW1hcmxlLCBmb3Jlc3QxOSA9PSAxIHwgd2V0bGFuZHMxOSA9PSAxICkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiTm90IFN1aXRhYmxlIiksCiAgZmlsdGVyKEFsYmVtYXJsZSwgZGV2ZWxvcGVkMTkgPT0gMSkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiRGV2ZWxvcGVkIikpCgpncmlkLmFycmFuZ2UoCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9QWxiZW1hcmxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoRGV2ZWxvcG1lbnRfRGVtYW5kLDUpKSksIGNvbG91cj1OQSkgKwogIGdlb21fcG9pbnQoZGF0YT1BbGJlbWFybGVfbGFuZFVzZSwgYWVzKHg9eHlDKEFsYmVtYXJsZV9sYW5kVXNlKVssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhBbGJlbWFybGVfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNiwgc2l6ZSA9IDAuMjUpICsKICBnZW9tX3NmKGRhdGE9c3RfaW50ZXJzZWN0aW9uKEN2aWxsZUhpZ2h3YXlzLGZpbHRlcihzdHVkeUFyZWFDb3VudGllcywgTkFNRT09IkFsYmVtYXJsZSIpKSwgc2l6ZT0yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IkRldmVsb3BtZW50X0RlbWFuZCIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXN1YnN0cihxdWludGlsZUJyZWFrcyhBbGJlbWFybGUsIkRldmVsb3BtZW50X0RlbWFuZCIpLDEsNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwicmVkIikpICsKICBsYWJzKHRpdGxlID0gIkRldmVsb3BtZW50IFBvdGVudGlhbCwgXG4yMDQwOiBBbGJlbWFybGUiKSArIG1hcFRoZW1lICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLCBjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSksCgpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPUFsYmVtYXJsZSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKHBvcF9DaGFuZ2UsNSkpKSwgY29sb3VyPU5BKSArCiAgZ2VvbV9wb2ludChkYXRhPUFsYmVtYXJsZV9sYW5kVXNlLCBhZXMoeD14eUMoQWxiZW1hcmxlX2xhbmRVc2UpWywxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9eHlDKEFsYmVtYXJsZV9sYW5kVXNlKVssMl0sIGNvbG91cj1MYW5kX1VzZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDE2LCBzaXplID0gMC4yNSkgKwogIGdlb21fc2YoZGF0YT1zdF9pbnRlcnNlY3Rpb24oQ3ZpbGxlSGlnaHdheXMsZmlsdGVyKHN0dWR5QXJlYUNvdW50aWVzLCBOQU1FPT0iQWxiZW1hcmxlIikpLCBzaXplPTIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iUG9wdWxhdGlvbl9DaGFuZ2UiLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1zdWJzdHIocXVpbnRpbGVCcmVha3MoQWxiZW1hcmxlLCJwb3BfQ2hhbmdlIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKwogIGxhYnModGl0bGUgPSAiUHJvamVjdGVkIFBvcHVsYXRpb24sIFxuMjA0MDogQWxiZW1hcmxlIikgKyBtYXBUaGVtZSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwgY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpLCBuY29sPTIpCgpgYGAKClRoZSBwbG90cyBhYm92ZSBhcmUgY3JlYXRlZCB1c2luZyBhIGBnZ3Bsb3RgIHRyaWNrIHRvIHNob3cgb3VyIGZpc2huZXQgZ3JpZCBjZWxscyBvdmVybGF5ZWQgYnkgY29sb3ItY29kZWQgcG9pbnRzLiAKCkZvciBjb21wYXJpc29uIHB1cnBvc2VzLCB0aGlzIHByb2Nlc3MgaXMgcmVwbGljYXRlZCBmb3IgQ2hhcmxvdHRlc3ZpbGxlIGNvdW50eSBiZWxvdy4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodD0gOCwgZmlnLndpZHRoPSAxMX0KQ2hhcmxvdHRlc3ZpbGxlIDwtCiAgZGF0MiAlPiUgICAjY2FsY3VsYXRlIHBvcHVsYXRpb24gY2hhbmdlCiAgICBsZWZ0X2pvaW4oY291bnR5UG9wdWxhdGlvbl8yMDQwKSAlPiUKICAgIG11dGF0ZShwcm9wb3J0aW9uX29mX2NvdW50eV9wb3AgPSB0b3RhbF9wb3B1bGF0aW9uMjAyMCAvIGNvdW50eV9wb3B1bGF0aW9uXzIwMjAsCiAgICAgICAgICAgcG9wXzIwNDAuaW5maWxsID0gcHJvcG9ydGlvbl9vZl9jb3VudHlfcG9wICogY291bnR5X3Byb2plY3Rpb25fMjA0MCwKICAgICAgICAgICBwb3BfQ2hhbmdlID0gKHBvcF8yMDQwLmluZmlsbCAtIHRvdGFsX3BvcHVsYXRpb24yMDIwKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1jb3VudHlfcHJvamVjdGlvbl8yMDQwLCAtY291bnR5X3BvcHVsYXRpb25fMjAyMCwgCiAgICAgICAgICAgICAgICAgIC1wcm9wb3J0aW9uX29mX2NvdW50eV9wb3AsIC1wb3BfMjA0MC5pbmZpbGwpICU+JQogICAgbXV0YXRlKERldmVsb3BtZW50X0RlbWFuZCA9IHByZWRpY3QoTW9kZWw2LCBkYXQyLCB0eXBlPSJyZXNwb25zZSIpKSAlPiUKICAgIGZpbHRlcihOQU1FID09ICJDaGFybG90dGVzdmlsbGUiKSAKCgpDaGFybG90dGVzdmlsbGVfbGFuZFVzZSA8LSByYmluZCgKICBmaWx0ZXIoQ2hhcmxvdHRlc3ZpbGxlLCBmb3Jlc3QxOSA9PSAxIHwgd2V0bGFuZHMxOSA9PSAxICkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiTm90IFN1aXRhYmxlIiksCiAgZmlsdGVyKENoYXJsb3R0ZXN2aWxsZSwgZGV2ZWxvcGVkMTkgPT0gMSkgJT4lCiAgZHBseXI6OnNlbGVjdCgpICU+JSBtdXRhdGUoTGFuZF9Vc2UgPSAiRGV2ZWxvcGVkIikpCgpncmlkLmFycmFuZ2UoCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Q2hhcmxvdHRlc3ZpbGxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoRGV2ZWxvcG1lbnRfRGVtYW5kLDUpKSksIGNvbG91cj1OQSkgKwogIGdlb21fcG9pbnQoZGF0YT1DaGFybG90dGVzdmlsbGVfbGFuZFVzZSwgYWVzKHg9eHlDKENoYXJsb3R0ZXN2aWxsZV9sYW5kVXNlKVssMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5PXh5QyhDaGFybG90dGVzdmlsbGVfbGFuZFVzZSlbLDJdLCBjb2xvdXI9TGFuZF9Vc2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAxNiwgc2l6ZSA9IDEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlNSwgbmFtZT0iRGV2ZWxvcG1lbnRfRGVtYW5kIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKENoYXJsb3R0ZXN2aWxsZSwiRGV2ZWxvcG1lbnRfRGVtYW5kIiksMSw1KSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCJyZWQiKSkgKwogIGxhYnModGl0bGUgPSAiRGV2ZWxvcG1lbnQgUG90ZW50aWFsLCBcbjIwNDA6IENoYXJsb3R0ZXN2aWxsZSIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwKCmdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Q2hhcmxvdHRlc3ZpbGxlLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUocG9wX0NoYW5nZSw1KSkpLCBjb2xvdXI9TkEpICsKICBnZW9tX3BvaW50KGRhdGE9Q2hhcmxvdHRlc3ZpbGxlX2xhbmRVc2UsIGFlcyh4PXh5QyhDaGFybG90dGVzdmlsbGVfbGFuZFVzZSlbLDFdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT14eUMoQ2hhcmxvdHRlc3ZpbGxlX2xhbmRVc2UpWywyXSwgY29sb3VyPUxhbmRfVXNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMTYsIHNpemUgPSAxKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTUsIG5hbWU9IlBvcHVsYXRpb25fQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c3Vic3RyKHF1aW50aWxlQnJlYWtzKEFsYmVtYXJsZSwicG9wX0NoYW5nZSIpLDEsNSkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwicmVkIikpICsKICBsYWJzKHRpdGxlID0gIlByb2plY3RlZCBQb3B1bGF0aW9uLCBcbjIwNDA6IENoYXJsb3R0ZXN2aWxsZSIpICsgbWFwVGhlbWUgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksIGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSwgbmNvbD0yKQoKYGBgCgojIDguIENvbmNsdXNpb24KCldlIHN0b3Agc2hvcnQgb2YgYWN0dWFsbHkgYWxsb2NhdGluZyBsYW5kIHRvIGRldmVsb3BtZW50LiBXaGlsZSBvdXIgbW9kZWwgaXMgd2VsbC1zdWl0ZWQgZm9yIHVuZGVyc3RhbmRpbmcgc3ByYXdsLXN0eWxlIGRldmVsb3BtZW50LCBpdCBpcyBub3QgdXNlZnVsIGZvciB1bmRlcnN0YW5kaW5nIGhvdyBuZXcgZGVtYW5kIG1pZ2h0IGJlIGFic29yYmVkIGJ5IHVwem9uaW5nIGFuZCBkZW5zaWZpY2F0aW9uIG9mIGV4aXN0aW5nIGRldmVsb3BtZW50LiBJdCB3b3VsZCBub3QgYmUgd2lzZSB0byBhbGxvY2F0ZSB0aGUgZW50aXJlIHByb2plY3RlZCBwb3B1bGF0aW9uIHRvIHVuZGV2ZWxvcGVkIGxhbmQuIEluc3RlYWQsIHdl4oCZZCBwcmVmZXIgYSBtb3JlIG51YW5jZWQgdW5kZXJzdGFuZGluZyBvZiBob3cgbG9jYWwgbGFuZCB1c2UgbGF3cyBtaWdodCBwbGF5IGEgcm9sZS4gQXQgdGhpcyBzdGFnZSBpbiB0aGUgYW5hbHlzaXMgaG93ZXZlciwgdGhlIFBsYW5uZXIgaGFzIGFsbCB0aGV5IG5lZWQgdG8gZW5nYWdlIGxvY2FsIHN0YWtlaG9sZGVycyBhYm91dCBmdXR1cmUgZGV2ZWxvcG1lbnQgZGVjaXNpb25zLgoKTmV4dCwgd2UgcmFuIHRoZSBzYW1lIHNjcmlwdCB3aXRoIGEgbmV3IGhpZ2h3YXlzIGRhdGFzZXQgdXBncmFkaW5nIGFuIGV4aXN0aW5nIHJvYWQgY29ubmVjdGluZyBDaGFybG90dGVzdmlsbGUgYW5kIENyb3pldCB0byBhIGhpZ2h3YXkgdG8gc2VlIGhvdyB0aGlzIG5ldyBpbmZyYXN0cnVjdHVyZSBtaWdodCBpbXBhY3QgdXJiYW4gZ3Jvd3RoIHByb2plY3Rpb25zLiBIZXJlIGFyZSB0aG9zZSByZXN1bHRzIGZvciBBbGJlbWFybGU6CgohW0FsYmVtYXJsZSBDb3VudHldKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ZaW5ndG9uZy1aL3NwcmF3bC1mb3JlY2FzdGluZy9jMzUxZTg4MDMxYWY1MDhjYTE5ZjEwODA3MWIwZTcwMDhlNGNjMzZjL2dyYXBoL0FsbG9jYXRpb24yLjNBLmpwZWcpCgpBbmQgaGVyZSBhcmUgdGhlIHJlc3VsdHMgZm9yIENoYXJsb3R0ZXN2aWxsZToKCiFbQ2hhcmxvdHRlc3ZpbGxlIENvdW50eV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1lpbmd0b25nLVovc3ByYXdsLWZvcmVjYXN0aW5nL2MzNTFlODgwMzFhZjUwOGNhMTlmMTA4MDcxYjBlNzAwOGU0Y2MzNmMvZ3JhcGgvQWxsb2NhdGlvbjIuM0MuanBlZykKCkh0bWwgZm9yIHRoaXMgc2NyaXB0IGlzIGxpbmtlZCBbaGVyZV0oaHR0cHM6Ly95aW5ndG9uZy16LmdpdGh1Yi5pby9zcHJhd2wtZm9yZWNhc3RpbmcvUi1zY3JpcHRzLzIuMy0tdXJiYW5fZ3Jvd3RoX21vZGVsaW5nX0NoYXJsb3R0ZXN2aWxsZS5odG1sKQ==